.PHONY: itest test
+test:
+ ZDOTDIR="${PWD}/tests" cram --shell=zsh -v tests
+
itest:
ZDOTDIR="${PWD}/tests" cram -i --shell=zsh tests
-test:
- ZDOTDIR="${PWD}/tests" cram --shell=zsh tests
+# Define targets for test files, with relative and abolute path.
+# Use verbose output, which is useful with Vim's 'errorformat'.
+TESTS:=$(wildcard tests/*.t)
+
+uniq = $(if $1,$(firstword $1) $(call uniq,$(filter-out $(firstword $1),$1)))
+_TESTS_REL_AND_ABS:=$(call uniq,$(abspath $(TESTS)) $(TESTS))
+$(_TESTS_REL_AND_ABS):
+ ZDOTDIR="${PWD}/tests" cram --shell=zsh -v $@
+.PHONY: $(_TESTS_REL_AND_ABS)
# Initially based on
# https://github.com/joshuaclayton/dotfiles/blob/master/zsh_profile.d/autoenv.zsh
-# TODO: move this to DOTENV_*?!
-export ENV_AUTHORIZATION_FILE=$HOME/.env_auth
+export AUTOENV_ENV_FILENAME=$HOME/.env_auth
# Name of file to look for when entering directories.
-: ${DOTENV_FILE_ENTER:=.env}
+: ${AUTOENV_FILE_ENTER:=.env}
# Name of file to look for when leaving directories.
-# Requires DOTENV_HANDLE_LEAVE=1.
-: ${DOTENV_FILE_LEAVE:=.env.leave}
+# Requires AUTOENV_HANDLE_LEAVE=1.
+: ${AUTOENV_FILE_LEAVE:=.env.leave}
# Look for .env in parent dirs?
-: ${DOTENV_LOOK_UPWARDS:=1}
+: ${AUTOENV_LOOK_UPWARDS:=1}
# Handle leave events when changing away from a subtree, where an "enter"
# event was handled?
-: ${DOTENV_HANDLE_LEAVE:=1}
+: ${AUTOENV_HANDLE_LEAVE:=1}
+# Enable debugging. Multiple levels are supported (max 2).
+: ${AUTOENV_DEBUG:=0}
-# Internal: stack of entered (and handled) directories.
-_dotenv_stack_entered=()
+# Public helper functions, which can be used from your .env 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.
+autoenv_source_parent() {
+ local parent_env_file=$(_autoenv_get_file_upwards $PWD)
+ if [[ -n $parent_env_file ]] \
+ && _autoenv_check_authorized_env_file $parent_env_file; then
+ _autoenv_debug "Calling autoenv_source_parent: parent_env_file:$parent_env_file"
-_dotenv_hash_pair() {
+ local parent_env_dir=${parent_env_file:A:h}
+
+ _autoenv_stack_entered_add $parent_env_file
+
+ _autoenv_source $parent_env_file enter $parent_env_dir
+ fi
+}
+
+# Internal functions. {{{
+# Internal: stack of entered (and handled) directories. {{{
+_autoenv_stack_entered=()
+typeset -A _autoenv_stack_entered_mtime
+_autoenv_stack_entered_mtime=()
+
+# Add an entry to the stack, and remember its mtime.
+_autoenv_stack_entered_add() {
local env_file=$1
- env_shasum=$(shasum $env_file | cut -d' ' -f1)
- echo "$env_file:$env_shasum"
+
+ _autoenv_debug "[stack] adding: $env_file" 2
+
+ # Remove any existing entry.
+ _autoenv_stack_entered_remove $env_file
+
+ # Append it to the stack, and remember its mtime.
+ _autoenv_stack_entered+=($env_file)
+ _autoenv_stack_entered_mtime[$env_file]=$(_autoenv_get_file_mtime $env_file)
+}
+
+_autoenv_get_file_mtime() {
+ if [[ -f $1 ]]; then
+ zstat +mtime $1
+ else
+ echo 0
+ fi
}
-_dotenv_authorized_env_file() {
+# Remove an entry from the stack.
+_autoenv_stack_entered_remove() {
local env_file=$1
- local pair=$(_dotenv_hash_pair $env_file)
- test -f $ENV_AUTHORIZATION_FILE \
- && \grep -qF $pair $ENV_AUTHORIZATION_FILE
+ _autoenv_debug "[stack] removing: $env_file" 2
+ _autoenv_stack_entered[$_autoenv_stack_entered[(i)$env_file]]=()
+ _autoenv_stack_entered_mtime[$env_file]=
}
-_dotenv_authorize() {
+# Is the given entry already in the stack?
+_autoenv_stack_entered_contains() {
local env_file=$1
- _dotenv_deauthorize $env_file
- _dotenv_hash_pair $env_file >> $ENV_AUTHORIZATION_FILE
+ if (( ${+_autoenv_stack_entered[(r)${env_file}]} )); then
+ # Entry is in stack.
+ if [[ $_autoenv_stack_entered_mtime[$env_file] == $(_autoenv_get_file_mtime $env_file) ]]; then
+ # Entry has the expected mtime.
+ return
+ fi
+ fi
+ return 1
}
+# }}}
-_dotenv_deauthorize() {
+# Internal function for debug output. {{{
+_autoenv_debug() {
+ local msg=$1
+ local level=${2:-1}
+ if [[ $AUTOENV_DEBUG -lt $level ]]; then
+ return
+ fi
+ # Load zsh color support.
+ if [[ -z $colors ]]; then
+ autoload colors
+ colors
+ fi
+ # Build $indent prefix.
+ local indent=
+ if [[ $_autoenv_debug_indent -gt 0 ]]; then
+ for i in {1..${_autoenv_debug_indent}}; do
+ indent=" $indent"
+ done
+ fi
+
+ # Split $msg by \n (not newline).
+ lines=(${(ps:\\n:)msg})
+ for line in $lines; do
+ echo -n "${fg_bold[blue]}[autoenv]${fg_no_bold[default]} " >&2
+ echo ${indent}${line} >&2
+ done
+}
+# }}}
+
+# Load zstat module, but only its builtin `zstat`.
+zmodload -F zsh/stat b:zstat
+
+
+_autoenv_hash_pair() {
+ local env_file=${1:A}
+ local env_shasum
+ if [[ -n $2 ]]; then
+ env_shasum=$2
+ else
+ env_shasum=$(shasum $env_file | cut -d' ' -f1)
+ fi
+ echo "$env_file:$env_shasum:1"
+}
+
+_autoenv_authorized_env_file() {
local env_file=$1
- if [[ -f $ENV_AUTHORIZATION_FILE ]]; then
- echo $(\grep -vF $env_file $ENV_AUTHORIZATION_FILE) > $ENV_AUTHORIZATION_FILE
+ local pair=$(_autoenv_hash_pair $env_file)
+ test -f $AUTOENV_ENV_FILENAME \
+ && \grep -qF $pair $AUTOENV_ENV_FILENAME
+}
+
+_autoenv_authorize() {
+ local env_file=$1
+ _autoenv_deauthorize $env_file
+ _autoenv_hash_pair $env_file >> $AUTOENV_ENV_FILENAME
+}
+
+_autoenv_deauthorize() {
+ local env_file=$1
+ if [[ -f $AUTOENV_ENV_FILENAME ]]; then
+ echo $(\grep -vF $env_file $AUTOENV_ENV_FILENAME) > $AUTOENV_ENV_FILENAME
fi
}
# This function can be mocked in tests
-_dotenv_read_answer() {
+_autoenv_ask_for_yes() {
local answer
- read -q answer
- echo $answer
+ read answer
+ if [[ $answer == "yes" ]]; then
+ return 0
+ else
+ return 1
+ fi
}
# Args: 1: absolute path to env file (resolved symlinks).
-_dotenv_check_authorized_env_file() {
+_autoenv_check_authorized_env_file() {
if ! [[ -f $1 ]]; then
return 1
fi
- if ! _dotenv_authorized_env_file $1; then
- echo "Attempting to load unauthorized env file: $1"
+ if ! _autoenv_authorized_env_file $1; then
+ echo "Attempting to load unauthorized env file!"
+ command ls -l $1
echo ""
echo "**********************************************"
echo ""
echo ""
echo "**********************************************"
echo ""
- echo -n "Would you like to authorize it? [y/N] "
+ echo -n "Would you like to authorize it? (type 'yes') "
- local answer=$(_dotenv_read_answer)
- echo
- if [[ $answer != 'y' ]]; then
+ if ! _autoenv_ask_for_yes; then
return 1
fi
- _dotenv_authorize $1
+ _autoenv_authorize $1
fi
return 0
}
-_dotenv_source() {
+# Get directory of this file (absolute, with resolved symlinks).
+_autoenv_source_dir=${0:A:h}
+
+_autoenv_source() {
local env_file=$1
- _dotenv_event=$2
- _dotenv_cwd=$PWD
+ _autoenv_event=$2
+ local _autoenv_envfile_dir=${3:-${1:A:h}}
+
+ _autoenv_from_dir=$_autoenv_chpwd_prev_dir
+ _autoenv_to_dir=$PWD
- builtin cd -q ${env_file:h}
+ # Source varstash library once.
+ if [[ -z "$functions[(I)autostash]" ]]; then
+ source $_autoenv_source_dir/lib/varstash
+ # NOTE: Varstash uses $PWD as default for varstash_dir, we might set it to
+ # ${env_file:h}.
+ fi
+
+ # Change to directory of env file, source it and cd back.
+ local new_dir=$PWD
+ builtin cd -q $_autoenv_envfile_dir
+ _autoenv_debug "== SOURCE: ${bold_color}$env_file${reset_color}\n PWD: $PWD"
+ (( _autoenv_debug_indent++ ))
source $env_file
- builtin cd -q $_dotenv_cwd
+ (( _autoenv_debug_indent-- ))
+ _autoenv_debug "== END SOURCE =="
+ builtin cd -q $new_dir
+
+ # Unset vars set for enter/leave scripts.
+ # This should not get done for recursion (via autoenv_source_parent),
+ # and can be useful to have in general after autoenv was used.
+ # unset _autoenv_event _autoenv_from_dir _autoenv_to_dir
+}
+
+_autoenv_get_file_upwards() {
+ local look_from=${1:-$PWD}
+ local look_for=${2:-$AUTOENV_FILE_ENTER}
+
+ # Manually look in parent dirs. An extended Zsh glob should use Y1 for
+ # performance reasons, which is only available in zsh-5.0.5-146-g9381bb6.
+ local last
+ local parent_dir="$look_from/.."
+ while true; do
+ parent_dir=${parent_dir:A}
+ if [[ $parent_dir == $last ]]; then
+ break
+ fi
+ parent_file="${parent_dir}/${look_for}"
- unset _dotenv_event _dotenv_cwd
+ if [[ -f $parent_file ]]; then
+ echo $parent_file
+ break
+ fi
+
+ last=$parent_dir
+ parent_dir="${parent_dir}/.."
+ done
}
-_dotenv_chpwd_handler() {
- local env_file="$PWD/$DOTENV_FILE_ENTER"
+
+_autoenv_chpwd_prev_dir=$PWD
+_autoenv_chpwd_handler() {
+ local env_file="$PWD/$AUTOENV_FILE_ENTER"
+
+ _autoenv_debug "Calling chpwd handler: PWD=$PWD"
# Handle leave event for previously sourced env files.
- if [[ $DOTENV_HANDLE_LEAVE == 1 ]] && (( $#_dotenv_stack_entered )); then
- for prev_dir in ${_dotenv_stack_entered}; do
+ if [[ $AUTOENV_HANDLE_LEAVE == 1 ]] && (( $#_autoenv_stack_entered )); then
+ local prev_file prev_dir
+ for prev_file in ${_autoenv_stack_entered}; do
+ prev_dir=${prev_file:A:h}
if ! [[ ${PWD}/ == ${prev_dir}/* ]]; then
- local env_file_leave=$prev_dir/$DOTENV_FILE_LEAVE
- if _dotenv_check_authorized_env_file $env_file_leave; then
- _dotenv_source $env_file_leave leave
+ local env_file_leave=$prev_dir/$AUTOENV_FILE_LEAVE
+ if _autoenv_check_authorized_env_file $env_file_leave; then
+ _autoenv_source $env_file_leave leave $prev_dir
fi
- # Remove this entry from the stack.
- _dotenv_stack_entered=(${_dotenv_stack_entered#$prev_dir})
+ _autoenv_stack_entered_remove $prev_file
fi
done
fi
- if ! [[ -f $env_file ]] && [[ $DOTENV_LOOK_UPWARDS == 1 ]]; then
- # Look for files in parent dirs, using an extended Zsh glob.
- setopt localoptions extendedglob
- local m
- m=((../)#${DOTENV_FILE_ENTER}(N))
- if (( $#m )); then
- env_file=${${m[1]}:A}
- else
+ if ! [[ -f $env_file ]] && [[ $AUTOENV_LOOK_UPWARDS == 1 ]]; then
+ env_file=$(_autoenv_get_file_upwards $PWD)
+ if [[ -z $env_file ]]; then
+ _autoenv_chpwd_prev_dir=$PWD
return
fi
fi
- if ! _dotenv_check_authorized_env_file $env_file; then
+ # Load the env file only once: check if $env_file is in the stack of entered
+ # directories.
+ if _autoenv_stack_entered_contains $env_file; then
+ _autoenv_debug "Already in stack: $env_file"
+ _autoenv_chpwd_prev_dir=$PWD
return
fi
- # Load the env file only once: check if $env_file's parent
- # is in $_dotenv_stack_entered.
- local env_file_dir=${env_file:A:h}
- if (( ${+_dotenv_stack_entered[(r)${env_file_dir}]} )); then
+ if ! _autoenv_check_authorized_env_file $env_file; then
+ _autoenv_chpwd_prev_dir=$PWD
return
fi
- _dotenv_stack_entered+=(${env_file_dir})
+ _autoenv_stack_entered_add $env_file
+
+ # Source the enter env file.
+ _autoenv_debug "Sourcing from chpwd handler: $env_file"
+ _autoenv_source $env_file enter
+
+ _autoenv_chpwd_prev_dir=$PWD
- _dotenv_source $env_file enter
+ (( _autoenv_debug_indent++ ))
}
+# }}}
autoload -U add-zsh-hook
-add-zsh-hook chpwd _dotenv_chpwd_handler
+add-zsh-hook chpwd _autoenv_chpwd_handler
# Look in current directory already.
-_dotenv_chpwd_handler
+_autoenv_chpwd_handler
--- /dev/null
+################################################################################
+# Stash/unstash support for per-directory variables
+#
+# Adopted for zsh-autoenv.
+#
+# Copyright (c) 2009,2012 Dave Olszewski <cxreg@pobox.com>
+# http://github.com/cxreg/smartcd
+#
+# This code is released under GPL v2 and the Artistic License, and
+# may be redistributed under the terms of either.
+#
+#
+# This library allows you to save the current value of a given environment
+# variable in a temporary location, so that you can modify it, and then
+# later restore its original value.
+#
+# Note that you will need to be in the same directory you were in when you
+# stashed in order to successfully unstash. This is because the temporary
+# variable is derived from your current working directory's path.
+#
+# Usage:
+# stash PATH
+# export PATH=/something/else
+# [...]
+# unstash PATH
+#
+# Note that this was written for use with, and works very well with,
+# smartcd. See the documentation there for examples.
+#
+# An alternate usage is `autostash' which will trigger autounstash when
+# leaving the directory, if combined with smartcd. This reduces the amount
+# of explicit configuration you need to provide:
+#
+# autostash PATH
+# export PATH=/something/else
+#
+# You may also do both operations on line line, leaving only the very succinct
+#
+# autostash PATH=/something/else
+#
+# If you attempt to stash the same value twice, a warning will be displayed
+# and the second stash will not occur. To make it happen anyway, pass -f
+# as the first argument to stash.
+#
+# $ stash FOO
+# $ stash FOO
+# You have already stashed FOO, please specify "-f" if you want to overwrite another stashed value
+# $ stash -f FOO
+# $
+#
+# This rule is a bit different if you are assigning a value and the variable
+# has already been stashed. In that case, the new value will be assigned, but
+# the stash will not be overwritten. This allows for non-conflicting chained
+# stash-assign rules.
+#
+################################################################################
+
+
+# Library functions, from smartcd's lib/core/arrays. {{{
+function apush() {
+ local var=$1; shift
+ eval "$var=(\${$var[@]} \"\$@\")"
+}
+
+function alen() {
+ local var=$1
+
+ if [[ -n $var ]]; then
+ eval "echo \${#$var[@]}"
+ fi
+}
+
+function afirst() {
+ setopt localoptions && setopt ksharrays
+ local var=$1
+
+ if [[ -n $var ]] && (( $(eval "echo \${#$var[@]}") >= 1 )); then
+ eval "echo \"\${$var""[0]}\""
+ fi
+}
+
+function ashift() {
+ setopt localoptions && setopt ksharrays
+ local var=$1
+
+ local _ashift_return=
+
+ if [[ -n $var ]] && (( $(eval "echo \${#$var[@]}") >= 1 )); then
+ eval "_ashift_return=\"\${$var""[0]}\""
+ eval "$var""[0]=()"
+
+ echo "$_ashift_return"
+ fi
+}
+# }}}
+
+
+function stash() {
+ if [[ $1 == "-f" ]]; then
+ local force=1; shift
+ fi
+
+ while [[ -n $1 ]]; do
+ if [[ $1 == "alias" && $2 =~ "=" ]]; then
+ shift
+ local _stashing_alias_assign=1
+ continue
+ fi
+
+ local stash_expression=$1
+ local stash_which=${stash_expression%%'='*}
+ local stash_name=$(_mangle_var $stash_which)
+
+ # Extract the value and make it double-quote safe
+ local stash_value=${stash_expression#*'='}
+ stash_value=${stash_value//\\/\\\\}
+ stash_value=${stash_value//\"/\\\"}
+ stash_value=${stash_value//\`/\\\`}
+ stash_value=${stash_value//\$/\\\$}
+
+ if [[ ( -n "$(eval echo '$__varstash_alias__'$stash_name)" ||
+ -n "$(eval echo '$__varstash_function__'$stash_name)" ||
+ -n "$(eval echo '$__varstash_array__'$stash_name)" ||
+ -n "$(eval echo '$__varstash_export__'$stash_name)" ||
+ -n "$(eval echo '$__varstash_variable__'$stash_name)" ||
+ -n "$(eval echo '$__varstash_nostash__'$stash_name)" )
+ && -z $force ]]; then
+
+ if [[ -z $already_stashed && ${already_stashed-_} == "_" ]]; then
+ local already_stashed=1
+ else
+ already_stashed=1
+ fi
+
+ if [[ $stash_which == $stash_expression ]]; then
+ if [[ -z $run_from_smartcd ]]; then
+ echo "You have already stashed $stash_which, please specify \"-f\" if you want to overwrite another stashed value"
+ fi
+
+ # Skip remaining work if we're not doing an assignment
+ shift
+ continue
+ fi
+ fi
+
+ # Handle any alias that may exist under this name
+ if [[ -z $already_stashed ]]; then
+ local alias_def="$(eval alias $stash_which 2>/dev/null)"
+ if [[ -n $alias_def ]]; then
+ alias_def=${alias_def#alias }
+ eval "__varstash_alias__$stash_name=\"$alias_def\""
+ local stashed=1
+ fi
+ fi
+ if [[ $stash_which != $stash_expression && -n $_stashing_alias_assign ]]; then
+ eval "alias $stash_which=\"$stash_value\""
+ fi
+
+ # Handle any function that may exist under this name
+ if [[ -z $already_stashed ]]; then
+ local function_def="$(declare -f $stash_which)"
+ if [[ -n $function_def ]]; then
+ # make function definition quote-safe. because we are going to evaluate the
+ # source with "echo -e", we need to double-escape the backslashes (so 1 -> 4)
+ function_def=${function_def//\\/\\\\\\\\}
+ function_def=${function_def//\"/\\\"}
+ function_def=${function_def//\`/\\\`}
+ function_def=${function_def//\$/\\\$}
+ eval "__varstash_function__$stash_name=\"$function_def\""
+ local stashed=1
+ fi
+ fi
+
+ # Handle any variable that may exist under this name
+ local vartype="$(declare -p $stash_which 2>/dev/null)"
+ if [[ -n $vartype ]]; then
+ if [[ -n $ZSH_VERSION ]]; then
+ local pattern="^typeset"
+ else
+ local pattern="^declare"
+ fi
+ if [[ $vartype =~ $pattern" -a" ]]; then
+ # varible is an array
+ if [[ -z $already_stashed ]]; then
+ eval "__varstash_array__$stash_name=(\"\${$stash_which""[@]}\")"
+ fi
+
+ elif [[ $vartype =~ $pattern" -x" ]]; then
+ # variable is exported
+ if [[ -z $already_stashed ]]; then
+ eval "__varstash_export__$stash_name=\"\$$stash_which\""
+ fi
+ if [[ $stash_which != $stash_expression && -z $_stashing_alias_assign ]]; then
+ eval "export $stash_which=\"$stash_value\""
+ fi
+ else
+ # regular variable
+ if [[ -z $already_stashed ]]; then
+ eval "__varstash_variable__$stash_name=\"\$$stash_which\""
+ fi
+ if [[ $stash_which != $stash_expression && -z $_stashing_alias_assign ]]; then
+ eval "$stash_which=\"$stash_value\""
+ fi
+
+ fi
+ local stashed=1
+ fi
+
+ if [[ -z $stashed ]]; then
+ # Nothing in the variable we're stashing, but make a note that we stashed so we
+ # do the right thing when unstashing. Without this, we take no action on unstash
+
+ # Zsh bug sometimes caues
+ # (eval):1: command not found: __varstash_nostash___tmp__home_dolszewski_src_smartcd_RANDOM_VARIABLE=1
+ # fixed in zsh commit 724fd07a67f, version 4.3.14
+ if [[ -z $already_stashed ]]; then
+ eval "__varstash_nostash__$stash_name=1"
+ fi
+
+ # In the case of a previously unset variable that we're assigning too, export it
+ if [[ $stash_which != $stash_expression && -z $_stashing_alias_assign ]]; then
+ eval "export $stash_which=\"$stash_value\""
+ fi
+ fi
+
+ shift
+ unset -v _stashing_alias_assign
+ done
+}
+
+function autostash() {
+ local run_from_autostash=1
+ while [[ -n $1 ]]; do
+ if [[ $1 == "alias" && $2 =~ "=" ]]; then
+ shift
+ local _stashing_alias_assign=1
+ fi
+
+ local already_stashed=
+ stash "$1"
+ if [[ -z $already_stashed ]]; then
+ local autostash_name=$(_mangle_var AUTOSTASH)
+ local varname=${1%%'='*}
+ apush $autostash_name "$varname"
+ fi
+ shift
+ unset -v _stashing_alias_assign
+ done
+}
+
+function unstash() {
+ while [[ -n $1 ]]; do
+ local unstash_which=$1
+ if [[ -z $unstash_which ]]; then
+ continue
+ fi
+
+ local unstash_name=$(_mangle_var $unstash_which)
+
+ # This bit is a little tricky. Here are the rules:
+ # 1) unstash any alias, function, or variable which matches
+ # 2) if one or more matches, but not all, delete any that did not
+ # 3) if none match but nostash is found, delete all
+ # 4) if none match and nostash not found, do nothing
+
+ # Unstash any alias
+ if [[ -n "$(eval echo \$__varstash_alias__$unstash_name)" ]]; then
+ eval "alias $(eval echo \$__varstash_alias__$unstash_name)"
+ unset __varstash_alias__$unstash_name
+ local unstashed=1
+ local unstashed_alias=1
+ fi
+
+ # Unstash any function
+ if [[ -n "$(eval echo \$__varstash_function__$unstash_name)" ]]; then
+ eval "function $(eval echo -e \"\$__varstash_function__$unstash_name\")"
+ unset __varstash_function__$unstash_name
+ local unstashed=1
+ local unstashed_function=1
+ fi
+
+ # Unstash any variable
+ if [[ -n "$(declare -p __varstash_array__$unstash_name 2>/dev/null)" ]]; then
+ eval "$unstash_which=(\"\${__varstash_array__$unstash_name""[@]}\")"
+ unset __varstash_array__$unstash_name
+ local unstashed=1
+ local unstashed_variable=1
+ elif [[ -n "$(declare -p __varstash_export__$unstash_name 2>/dev/null)" ]]; then
+ eval "export $unstash_which=\"\$__varstash_export__$unstash_name\""
+ unset __varstash_export__$unstash_name
+ local unstashed=1
+ local unstashed_variable=1
+ elif [[ -n "$(declare -p __varstash_variable__$unstash_name 2>/dev/null)" ]]; then
+ # Unset variable first to reset export
+ unset -v $unstash_which
+ eval "$unstash_which=\"\$__varstash_variable__$unstash_name\""
+ unset __varstash_variable__$unstash_name
+ local unstashed=1
+ local unstashed_variable=1
+ fi
+
+ # Unset any values which did not exist at time of stash
+ local nostash="$(eval echo \$__varstash_nostash__$unstash_name)"
+ unset __varstash_nostash__$unstash_name
+ if [[ ( -n "$nostash" && -z "$unstashed" ) || ( -n "$unstashed" && -z "$unstashed_alias" ) ]]; then
+ unalias $unstash_which 2>/dev/null
+ fi
+ if [[ ( -n "$nostash" && -z "$unstashed" ) || ( -n "$unstashed" && -z "$unstashed_function" ) ]]; then
+ unset -f $unstash_which 2>/dev/null
+ fi
+ if [[ ( -n "$nostash" && -z "$unstashed" ) || ( -n "$unstashed" && -z "$unstashed_variable" ) ]]; then
+ # Don't try to unset illegal variable names
+ if ! [[ $unstash_which =~ [^a-zA-Z0-9_] || $unstash_which =~ ^[0-9] ]]; then
+ unset -v $unstash_which
+ fi
+ fi
+
+ shift
+ done
+}
+
+function autounstash() {
+ # If there is anything in (mangled) variable AUTOSTASH, then unstash it
+ local autounstash_name=$(_mangle_var AUTOSTASH)
+ if (( $(alen $autounstash_name) > 0 )); then
+ local run_from_autounstash=1
+ while (( $(alen $autounstash_name) > 0 )); do
+ local autounstash_var=$(afirst $autounstash_name)
+ ashift $autounstash_name >/dev/null
+ unstash $autounstash_var
+ done
+ unset $autounstash_name
+ fi
+}
+
+function _mangle_var() {
+ local mangle_var_where="${varstash_dir:-$PWD}"
+ mangle_var_where=${mangle_var_where//[^A-Za-z0-9]/_}
+ local mangled_name=${1//[^A-Za-z0-9]/_}
+ echo "_tmp_${mangle_var_where}_${mangled_name}"
+}
+
+# vim: filetype=sh autoindent expandtab shiftwidth=4 softtabstop=4
test -f "$TESTDIR/.zcompdump" && rm "$TESTDIR/.zcompdump"
+AUTOENV_DEBUG=0
+
source "$TESTDIR/../autoenv.plugin.zsh"
-export ENV_AUTHORIZATION_FILE="$PWD/.env_auth"
+export AUTOENV_ENV_FILENAME="$PWD/.env_auth"
--- /dev/null
+Tests for internal stack handling.
+
+ $ source $TESTDIR/setup.sh
+
+Non-existing entries are allowed and handled without error.
+
+ $ _autoenv_stack_entered_add non-existing
+ $ echo $_autoenv_stack_entered
+ non-existing
+
+Add existing entries.
+
+ $ mkdir -p sub/sub2
+ $ touch -t 201401010101 sub/file
+ $ _autoenv_stack_entered_add sub
+ $ _autoenv_stack_entered_add sub/file
+ $ _autoenv_stack_entered_add sub/sub2
+ $ echo $_autoenv_stack_entered
+ non-existing sub sub/file sub/sub2
+
+ $ _autoenv_stack_entered_add non-existing
+ $ echo $_autoenv_stack_entered
+ sub sub/file sub/sub2 non-existing
+
+ $ echo ${(k)_autoenv_stack_entered}
+ sub sub/file sub/sub2 non-existing
+
+ $ echo $_autoenv_stack_entered_mtime
+ 0 1388538060 0 0 (glob)
+
+Touch the file and re-add it.
+
+ $ touch -t 201401012359 sub/file
+ $ _autoenv_stack_entered_add sub/file
+
+The mtime should have been updated.
+
+ $ echo ${_autoenv_stack_entered_mtime[sub/file]}
+ 1388620740
+
+It should have moved to the end of the stack.
+
+ $ echo ${(k)_autoenv_stack_entered}
+ sub sub/sub2 non-existing sub/file
+
+Test lookup of containing elements.
+
+ $ _autoenv_stack_entered_contains sub/file
+ $ _autoenv_stack_entered_contains non-existing
+ $ _autoenv_stack_entered_contains not-added
+ [1]
+
+Test removing.
+
+ $ _autoenv_stack_entered_remove sub
+ $ echo ${_autoenv_stack_entered}
+ sub/sub2 non-existing sub/file
+
--- /dev/null
+Tests for internal util methods.
+
+ $ source $TESTDIR/setup.sh
+
+Non-existing entries are allowed and handled without error.
+
+ $ mkdir -p sub/sub2
+ $ touch file sub/file sub/sub2/file
+
+Should not get the file from the current dir.
+ $ _autoenv_get_file_upwards . file
+
+ $ cd sub/sub2
+ $ _autoenv_get_file_upwards . file
+ */_autoenv_utils.t/sub/file (glob)
-Ensure we have our mocked out ENV_AUTHORIZATION_FILE
-
- $ [[ $ENV_AUTHORIZATION_FILE[0,4] == '/tmp' ]] || return 1
+ $ source $TESTDIR/setup.sh
Lets set a simple .env action
Manually create auth file
- $ echo "$PWD/.env:$(echo echo ENTERED | shasum)" > $ENV_AUTHORIZATION_FILE
+ $ test_autoenv_add_to_env $PWD/.env
$ cd .
ENTERED
Now try to make it accept it
- $ unset _dotenv_stack_entered
- $ rm $ENV_AUTHORIZATION_FILE
- $ _dotenv_read_answer() { echo 'y' }
+ $ unset _autoenv_stack_entered
+ $ rm $AUTOENV_ENV_FILENAME
+ $ _autoenv_ask_for_yes() { echo "yes" }
$ cd .
- Attempting to load unauthorized env file: /tmp/cramtests-??????/autoenv.t/.env (glob)
+ Attempting to load unauthorized env file!
+ -* /tmp/cramtests-*/autoenv.t/.env (glob)
**********************************************
**********************************************
- Would you like to authorize it? [y/N]
+ Would you like to authorize it? (type 'yes') yes
ENTERED
+The last "ENTERED" is because it executed the command.
+Now lets see that it actually checks the shasum value.
-
-The last "ENTERED" is because it executed the command
-
-Now lets see that it actually checks the shasum value
-
- $ unset _dotenv_stack_entered
+ $ unset _autoenv_stack_entered
$ cd .
ENTERED
- $ unset _dotenv_stack_entered
- $ rm $ENV_AUTHORIZATION_FILE
- $ echo "$PWD/.env:$(echo mischief | shasum)" > $ENV_AUTHORIZATION_FILE
+ $ unset _autoenv_stack_entered
+ $ rm $AUTOENV_ENV_FILENAME
+ $ test_autoenv_add_to_env $PWD/.env mischief
$ cd .
- Attempting to load unauthorized env file: /tmp/cramtests-??????/autoenv.t/.env (glob)
+ Attempting to load unauthorized env file!
+ -* /tmp/cramtests-*/autoenv.t/.env (glob)
**********************************************
**********************************************
- Would you like to authorize it? [y/N]
+ Would you like to authorize it? (type 'yes') yes
ENTERED
-
-
-
Now, will it take no for an answer?
- $ unset _dotenv_stack_entered
- $ rm $ENV_AUTHORIZATION_FILE
- $ _dotenv_read_answer() { echo 'n' }
+ $ unset _autoenv_stack_entered
+ $ rm $AUTOENV_ENV_FILENAME
+ $ _autoenv_ask_for_yes() { echo "no"; return 1 }
$ cd .
- Attempting to load unauthorized env file: /tmp/cramtests-??????/autoenv.t/.env (glob)
+ Attempting to load unauthorized env file!
+ -* /tmp/cramtests-*/autoenv.t/.env (glob)
**********************************************
**********************************************
- Would you like to authorize it? [y/N]
-
-
-
+ Would you like to authorize it? (type 'yes') no
-Lets also try one more time to ensure it didnt add it
+Lets also try one more time to ensure it didn't add it.
$ cd .
- Attempting to load unauthorized env file: /tmp/cramtests-??????/autoenv.t/.env (glob)
+ Attempting to load unauthorized env file!
+ -* /tmp/cramtests-*/autoenv.t/.env (glob)
**********************************************
**********************************************
- Would you like to authorize it? [y/N]
+ Would you like to authorize it? (type 'yes') no
-Test $PWD and $_dotenv_cwd.
+Test $PWD, $_autoenv_from_dir and _autoenv_to_dir.
-Ensure we have our mocked out ENV_AUTHORIZATION_FILE.
-
- $ [[ $ENV_AUTHORIZATION_FILE[0,4] == '/tmp' ]] || return 1
+ $ source $TESTDIR/setup.sh
Setup env actions / output.
- $ DOTENV_LOOK_UPWARDS=1
+ $ AUTOENV_LOOK_UPWARDS=1
$ mkdir -p sub/sub2
$ cd sub
- $ echo 'echo ENTERED: cwd:${PWD:t} ${_dotenv_cwd:t}' >> .env
- $ echo 'echo LEFT: cwd:${PWD:t} ${_dotenv_cwd:t}' >> .env.leave
+ $ echo 'echo ENTERED: PWD:${PWD:t} from:${_autoenv_from_dir:t} to:${_autoenv_to_dir:t}' > .env
+ $ echo 'echo LEFT: PWD:${PWD:t} from:${_autoenv_from_dir:t} to:${_autoenv_to_dir:t}' > .env.leave
Manually create auth files.
- $ echo "$PWD/$DOTENV_FILE_ENTER:$(echo $(<$DOTENV_FILE_ENTER) | shasum)" > $ENV_AUTHORIZATION_FILE
- $ echo "$PWD/$DOTENV_FILE_LEAVE:$(echo $(<$DOTENV_FILE_LEAVE) | shasum)" >> $ENV_AUTHORIZATION_FILE
+ $ test_autoenv_auth_env_files
The actual tests.
$ cd .
- ENTERED: cwd:sub sub
+ ENTERED: PWD:sub from:sub to:sub
$ cd ..
- LEFT: cwd:sub cwd.t
+ LEFT: PWD:sub from:sub to:cwd.t
$ cd sub/sub2
- ENTERED: cwd:sub sub2
+ ENTERED: PWD:sub from:cwd.t to:sub2
-Ensure we have our mocked out ENV_AUTHORIZATION_FILE
-
- $ [[ $ENV_AUTHORIZATION_FILE[0,4] == '/tmp' ]] || return 1
-
+ $ source $TESTDIR/setup.sh
Lets set a simple .env action
Change to the directory.
- $ _dotenv_read_answer() { echo 'y' }
+ $ _autoenv_ask_for_yes() { echo "yes"; return 0 }
$ cd .
- Attempting to load unauthorized env file: /tmp/cramtests-??????/leave.t/sub/.env (glob)
+ Attempting to load unauthorized env file!
+ -* /tmp/cramtests-*/leave.t/sub/.env (glob)
**********************************************
**********************************************
- Would you like to authorize it? [y/N]
+ Would you like to authorize it? (type 'yes') yes
ENTERED
Leave the directory and answer "no".
- $ _dotenv_read_answer() { echo 'n' }
+ $ _autoenv_ask_for_yes() { echo "no"; return 1 }
$ cd ..
- Attempting to load unauthorized env file: /tmp/cramtests-??????/leave.t/sub/.env.leave (glob)
+ Attempting to load unauthorized env file!
+ -* /tmp/cramtests-*/leave.t/sub/.env.leave (glob)
**********************************************
**********************************************
- Would you like to authorize it? [y/N]
+ Would you like to authorize it? (type 'yes') no
$ cd sub
ENTERED
- $ _dotenv_read_answer() { echo 'y' }
+ $ _autoenv_ask_for_yes() { echo "yes"; return 0 }
$ cd ..
- Attempting to load unauthorized env file: /tmp/cramtests-??????/leave.t/sub/.env.leave (glob)
+ Attempting to load unauthorized env file!
+ -* /tmp/cramtests-*/leave.t/sub/.env.leave (glob)
**********************************************
**********************************************
- Would you like to authorize it? [y/N]
+ Would you like to authorize it? (type 'yes') yes
LEFT
Now check with subdirs, looking upwards.
- $ DOTENV_LOOK_UPWARDS=1
+ $ AUTOENV_LOOK_UPWARDS=1
$ mkdir sub/child
$ cd sub/child
ENTERED
Now check with subdirs, not looking at parent dirs.
- $ DOTENV_LOOK_UPWARDS=0
+ $ AUTOENV_LOOK_UPWARDS=0
$ cd sub/child
$ cd ..
ENTERED
LEFT
-Test that .env is sourced only once with DOTENV_HANDLE_LEAVE=0.
+Test that .env is sourced only once with AUTOENV_HANDLE_LEAVE=0.
- $ unset _dotenv_stack_entered
- $ DOTENV_HANDLE_LEAVE=0
+ $ unset _autoenv_stack_entered
+ $ AUTOENV_HANDLE_LEAVE=0
$ cd sub
ENTERED
$ cd ..
--- /dev/null
+Test recursing into parent .env files.
+
+ $ source $TESTDIR/setup.sh
+
+Setup env actions / output.
+
+ $ AUTOENV_LOOK_UPWARDS=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
+ $ test_autoenv_auth_env_files
+
+Create env files in sub dir.
+
+ $ mkdir -p sub/sub2
+ $ cd sub
+ ENTERED_root: PWD:recurse-upwards.t 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
+ $ test_autoenv_auth_env_files
+
+The actual tests.
+
+ $ cd .
+ ENTERED_sub: PWD:sub from:sub to:sub
+
+ $ cd ..
+ LEFT_sub: PWD:sub from:sub to:recurse-upwards.t
+
+ $ cd sub/sub2
+ ENTERED_sub: PWD:sub from:recurse-upwards.t to:sub2
+
+ $ cd ..
+
+Changing the .env file should re-source it.
+
+ $ echo 'echo ENTER2' >> .env
+
+Set timestamp of auth file into the past, so it gets seen as new below.
+
+ $ touch -t 201401010101 .env
+
+ $ 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.
+
+ $ echo "echo autoenv_source_parent_from_sub2:\nautoenv_source_parent\necho done_sub2\n" > sub2/.env
+ $ test_autoenv_add_to_env sub2/.env
+ $ cd sub2
+ autoenv_source_parent_from_sub2:
+ ENTERED_sub: PWD:sub from:sub to:sub2
+ ENTER2
+ done_sub2
+
+Move sub/.env away, now the root .env file should get sourced.
+
+ $ mv ../.env ../.env.out
+ $ touch -t 201401010102 .env
+ $ cd .
+ autoenv_source_parent_from_sub2:
+ ENTERED_root: PWD:recurse-upwards.t from:sub2 to:sub2
+ done_sub2
+ $ mv ../.env.out ../.env
+
+Prepend call to autoenv_source_parent to sub/.env 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
+ $ test_autoenv_auth_env_files
+
+ $ cd .
+ autoenv_source_parent_from_sub:
+ ENTERED_root: PWD:recurse-upwards.t from:sub to:sub
+ ENTERED_sub: PWD:sub from:sub to:sub
+ ENTER2
+ done_sub
+
+
+Add sub/sub2/.env 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
+ $ cd sub2
+ autoenv_source_parent_from_sub2:
+ autoenv_source_parent_from_sub:
+ ENTERED_root: PWD:recurse-upwards.t from:sub to:sub
+ ENTERED_sub: PWD:sub from:sub to:sub
+ ENTER2
+ done_sub
+ done_sub2
+
+Go to root.
+This should not trigger the enter event, because it was handled via
+autoenv_source_parent already.
+
+ $ cd ../..
+ LEFT_sub: PWD:sub from:sub2 to:recurse-upwards.t
+
+
+Changing the root .env should trigger re-authentication via autoenv_source_parent.
+
+First, let's answer "no".
+
+ $ echo "echo NEW" > .env
+ $ _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)
+
+ **********************************************
+
+ echo NEW
+
+ **********************************************
+
+ Would you like to authorize it? (type 'yes') no
+ ENTERED_sub: PWD:sub from:recurse-upwards.t to:sub
+ ENTER2
+ done_sub
+
+Now with "yes".
+This currently does not trigger re-execution of the .env file.
+
+ $ _autoenv_ask_for_yes() { echo "yes"; return 0 }
+ $ cd .
+
+Touching the .env file will now source the parent env file.
+
+ $ touch -t 201401010104 .env
+ $ cd .
+ autoenv_source_parent_from_sub:
+ Attempting to load unauthorized env file!
+ -* /tmp/cramtests-*/recurse-upwards.t/.env (glob)
+
+ **********************************************
+
+ echo NEW
+
+ **********************************************
+
+ Would you like to authorize it? (type 'yes') yes
+ NEW
+ ENTERED_sub: PWD:sub from:sub to:sub
+ ENTER2
+ done_sub
+
+
+ $ cd ..
+ LEFT_sub: PWD:sub from:sub to:recurse-upwards.t
+ $ mkdir sub/sub2/sub3
+ $ cd sub/sub2/sub3
+ autoenv_source_parent_from_sub2:
+ autoenv_source_parent_from_sub:
+ NEW
+ ENTERED_sub: PWD:sub from:recurse-upwards.t to:sub
+ ENTER2
+ done_sub
+ done_sub2
--- /dev/null
+# Ensure we have our mocked out AUTOENV_ENV_FILENAME
+# (via .zshenv).
+
+[[ $AUTOENV_ENV_FILENAME[0,4] == '/tmp' ]] || return 1
+
+# Reset any authentication.
+echo -n > $AUTOENV_ENV_FILENAME
+
+# Add file $1 (with optional hash $2) to authentication file.
+test_autoenv_add_to_env() {
+ _autoenv_hash_pair $1 $2 >> $AUTOENV_ENV_FILENAME
+}
+
+# Add enter and leave env files to authentication file.
+test_autoenv_auth_env_files() {
+ test_autoenv_add_to_env $PWD/$AUTOENV_FILE_ENTER
+ test_autoenv_add_to_env $PWD/$AUTOENV_FILE_LEAVE
+}
--- /dev/null
+Test varstash integration.
+
+ $ source $TESTDIR/setup.sh
+
+Setup test environment.
+
+ $ mkdir sub
+ $ cd sub
+ $ echo 'echo ENTER; autostash FOO=baz' > $AUTOENV_FILE_ENTER
+ $ echo 'echo LEAVE; autounstash' > $AUTOENV_FILE_LEAVE
+
+Manually create auth file
+
+ $ test_autoenv_auth_env_files
+
+Set environment variable.
+
+ $ FOO=bar
+
+Activating the env stashes it and applies a new value.
+
+ $ cd .
+ ENTER
+ $ echo $FOO
+ baz
+
+Leaving the directory unstashes it.
+
+ $ cd ..
+ LEAVE
+ $ echo $FOO
+ bar