1
0
mirror of https://github.com/Tarrasch/zsh-autoenv.git synced 2024-06-29 11:10:11 +03:00

Merge pull request #8 from blueyed/next

Next: varstash integration, tests refactoring, s/dotenv/autoenv/ and some more
This commit is contained in:
Daniel Hahler 2014-11-27 15:52:17 +01:00
commit c2a3009d4a
12 changed files with 933 additions and 134 deletions

View File

@ -1,7 +1,17 @@
.PHONY: itest test .PHONY: itest test
test:
ZDOTDIR="${PWD}/tests" cram --shell=zsh -v tests
itest: itest:
ZDOTDIR="${PWD}/tests" cram -i --shell=zsh tests ZDOTDIR="${PWD}/tests" cram -i --shell=zsh tests
test: # Define targets for test files, with relative and abolute path.
ZDOTDIR="${PWD}/tests" cram --shell=zsh tests # 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)

View File

@ -1,68 +1,177 @@
# Initially based on # Initially based on
# https://github.com/joshuaclayton/dotfiles/blob/master/zsh_profile.d/autoenv.zsh # https://github.com/joshuaclayton/dotfiles/blob/master/zsh_profile.d/autoenv.zsh
# TODO: move this to DOTENV_*?! export AUTOENV_ENV_FILENAME=$HOME/.env_auth
export ENV_AUTHORIZATION_FILE=$HOME/.env_auth
# Name of file to look for when entering directories. # 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. # Name of file to look for when leaving directories.
# Requires DOTENV_HANDLE_LEAVE=1. # Requires AUTOENV_HANDLE_LEAVE=1.
: ${DOTENV_FILE_LEAVE:=.env.leave} : ${AUTOENV_FILE_LEAVE:=.env.leave}
# Look for .env in parent dirs? # 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" # Handle leave events when changing away from a subtree, where an "enter"
# event was handled? # 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. # Public helper functions, which can be used from your .env files:
_dotenv_stack_entered=() #
# 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}
local env_file=$1
env_shasum=$(shasum $env_file | cut -d' ' -f1) _autoenv_stack_entered_add $parent_env_file
echo "$env_file:$env_shasum"
_autoenv_source $parent_env_file enter $parent_env_dir
fi
} }
_dotenv_authorized_env_file() { # 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 local env_file=$1
local pair=$(_dotenv_hash_pair $env_file)
test -f $ENV_AUTHORIZATION_FILE \ _autoenv_debug "[stack] adding: $env_file" 2
&& \grep -qF $pair $ENV_AUTHORIZATION_FILE
# 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)
} }
_dotenv_authorize() { _autoenv_get_file_mtime() {
local env_file=$1 if [[ -f $1 ]]; then
_dotenv_deauthorize $env_file zstat +mtime $1
_dotenv_hash_pair $env_file >> $ENV_AUTHORIZATION_FILE else
echo 0
fi
} }
_dotenv_deauthorize() { # Remove an entry from the stack.
_autoenv_stack_entered_remove() {
local env_file=$1 local env_file=$1
if [[ -f $ENV_AUTHORIZATION_FILE ]]; then _autoenv_debug "[stack] removing: $env_file" 2
echo $(\grep -vF $env_file $ENV_AUTHORIZATION_FILE) > $ENV_AUTHORIZATION_FILE _autoenv_stack_entered[$_autoenv_stack_entered[(i)$env_file]]=()
_autoenv_stack_entered_mtime[$env_file]=
}
# Is the given entry already in the stack?
_autoenv_stack_entered_contains() {
local env_file=$1
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
}
# }}}
# 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
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 fi
} }
# This function can be mocked in tests # This function can be mocked in tests
_dotenv_read_answer() { _autoenv_ask_for_yes() {
local answer local answer
read -q answer read answer
echo $answer if [[ $answer == "yes" ]]; then
return 0
else
return 1
fi
} }
# Args: 1: absolute path to env file (resolved symlinks). # Args: 1: absolute path to env file (resolved symlinks).
_dotenv_check_authorized_env_file() { _autoenv_check_authorized_env_file() {
if ! [[ -f $1 ]]; then if ! [[ -f $1 ]]; then
return 1 return 1
fi fi
if ! _dotenv_authorized_env_file $1; then if ! _autoenv_authorized_env_file $1; then
echo "Attempting to load unauthorized env file: $1" echo "Attempting to load unauthorized env file!"
command ls -l $1
echo "" echo ""
echo "**********************************************" echo "**********************************************"
echo "" echo ""
@ -70,78 +179,133 @@ _dotenv_check_authorized_env_file() {
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) if ! _autoenv_ask_for_yes; then
echo
if [[ $answer != 'y' ]]; then
return 1 return 1
fi fi
_dotenv_authorize $1 _autoenv_authorize $1
fi fi
return 0 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 local env_file=$1
_dotenv_event=$2 _autoenv_event=$2
_dotenv_cwd=$PWD local _autoenv_envfile_dir=${3:-${1:A:h}}
builtin cd -q ${env_file:h} _autoenv_from_dir=$_autoenv_chpwd_prev_dir
_autoenv_to_dir=$PWD
# 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 source $env_file
builtin cd -q $_dotenv_cwd (( _autoenv_debug_indent-- ))
_autoenv_debug "== END SOURCE =="
builtin cd -q $new_dir
unset _dotenv_event _dotenv_cwd # 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
} }
_dotenv_chpwd_handler() { _autoenv_get_file_upwards() {
local env_file="$PWD/$DOTENV_FILE_ENTER" 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}"
if [[ -f $parent_file ]]; then
echo $parent_file
break
fi
last=$parent_dir
parent_dir="${parent_dir}/.."
done
}
_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. # Handle leave event for previously sourced env files.
if [[ $DOTENV_HANDLE_LEAVE == 1 ]] && (( $#_dotenv_stack_entered )); then if [[ $AUTOENV_HANDLE_LEAVE == 1 ]] && (( $#_autoenv_stack_entered )); then
for prev_dir in ${_dotenv_stack_entered}; do local prev_file prev_dir
for prev_file in ${_autoenv_stack_entered}; do
prev_dir=${prev_file:A:h}
if ! [[ ${PWD}/ == ${prev_dir}/* ]]; then if ! [[ ${PWD}/ == ${prev_dir}/* ]]; then
local env_file_leave=$prev_dir/$DOTENV_FILE_LEAVE local env_file_leave=$prev_dir/$AUTOENV_FILE_LEAVE
if _dotenv_check_authorized_env_file $env_file_leave; then if _autoenv_check_authorized_env_file $env_file_leave; then
_dotenv_source $env_file_leave leave _autoenv_source $env_file_leave leave $prev_dir
fi fi
# Remove this entry from the stack. _autoenv_stack_entered_remove $prev_file
_dotenv_stack_entered=(${_dotenv_stack_entered#$prev_dir})
fi fi
done done
fi fi
if ! [[ -f $env_file ]] && [[ $DOTENV_LOOK_UPWARDS == 1 ]]; then if ! [[ -f $env_file ]] && [[ $AUTOENV_LOOK_UPWARDS == 1 ]]; then
# Look for files in parent dirs, using an extended Zsh glob. env_file=$(_autoenv_get_file_upwards $PWD)
setopt localoptions extendedglob if [[ -z $env_file ]]; then
local m _autoenv_chpwd_prev_dir=$PWD
m=((../)#${DOTENV_FILE_ENTER}(N))
if (( $#m )); then
env_file=${${m[1]}:A}
else
return return
fi fi
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 return
fi fi
# Load the env file only once: check if $env_file's parent if ! _autoenv_check_authorized_env_file $env_file; then
# is in $_dotenv_stack_entered. _autoenv_chpwd_prev_dir=$PWD
local env_file_dir=${env_file:A:h}
if (( ${+_dotenv_stack_entered[(r)${env_file_dir}]} )); then
return return
fi fi
_dotenv_stack_entered+=(${env_file_dir}) _autoenv_stack_entered_add $env_file
_dotenv_source $env_file enter # Source the enter env file.
_autoenv_debug "Sourcing from chpwd handler: $env_file"
_autoenv_source $env_file enter
_autoenv_chpwd_prev_dir=$PWD
(( _autoenv_debug_indent++ ))
} }
# }}}
autoload -U add-zsh-hook autoload -U add-zsh-hook
add-zsh-hook chpwd _dotenv_chpwd_handler add-zsh-hook chpwd _autoenv_chpwd_handler
# Look in current directory already. # Look in current directory already.
_dotenv_chpwd_handler _autoenv_chpwd_handler

343
lib/varstash Normal file
View File

@ -0,0 +1,343 @@
################################################################################
# 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

View File

@ -1,4 +1,6 @@
test -f "$TESTDIR/.zcompdump" && rm "$TESTDIR/.zcompdump" test -f "$TESTDIR/.zcompdump" && rm "$TESTDIR/.zcompdump"
AUTOENV_DEBUG=0
source "$TESTDIR/../autoenv.plugin.zsh" source "$TESTDIR/../autoenv.plugin.zsh"
export ENV_AUTHORIZATION_FILE="$PWD/.env_auth" export AUTOENV_ENV_FILENAME="$PWD/.env_auth"

58
tests/_autoenv_stack.t Normal file
View File

@ -0,0 +1,58 @@
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

15
tests/_autoenv_utils.t Normal file
View File

@ -0,0 +1,15 @@
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)

View File

@ -1,6 +1,4 @@
Ensure we have our mocked out ENV_AUTHORIZATION_FILE $ source $TESTDIR/setup.sh
$ [[ $ENV_AUTHORIZATION_FILE[0,4] == '/tmp' ]] || return 1
Lets set a simple .env action Lets set a simple .env action
@ -8,17 +6,18 @@ Lets set a simple .env action
Manually create auth file Manually create auth file
$ echo "$PWD/.env:$(echo echo ENTERED | shasum)" > $ENV_AUTHORIZATION_FILE $ test_autoenv_add_to_env $PWD/.env
$ cd . $ cd .
ENTERED ENTERED
Now try to make it accept it Now try to make it accept it
$ unset _dotenv_stack_entered $ unset _autoenv_stack_entered
$ rm $ENV_AUTHORIZATION_FILE $ rm $AUTOENV_ENV_FILENAME
$ _dotenv_read_answer() { echo 'y' } $ _autoenv_ask_for_yes() { echo "yes" }
$ cd . $ 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)
********************************************** **********************************************
@ -26,26 +25,24 @@ Now try to make it accept it
********************************************** **********************************************
Would you like to authorize it? [y/N] Would you like to authorize it? (type 'yes') yes
ENTERED ENTERED
The last "ENTERED" is because it executed the command.
Now lets see that it actually checks the shasum value.
$ unset _autoenv_stack_entered
The last "ENTERED" is because it executed the command
Now lets see that it actually checks the shasum value
$ unset _dotenv_stack_entered
$ cd . $ cd .
ENTERED ENTERED
$ unset _dotenv_stack_entered $ unset _autoenv_stack_entered
$ rm $ENV_AUTHORIZATION_FILE $ rm $AUTOENV_ENV_FILENAME
$ echo "$PWD/.env:$(echo mischief | shasum)" > $ENV_AUTHORIZATION_FILE $ test_autoenv_add_to_env $PWD/.env mischief
$ cd . $ 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)
********************************************** **********************************************
@ -53,20 +50,18 @@ Now lets see that it actually checks the shasum value
********************************************** **********************************************
Would you like to authorize it? [y/N] Would you like to authorize it? (type 'yes') yes
ENTERED ENTERED
Now, will it take no for an answer? Now, will it take no for an answer?
$ unset _dotenv_stack_entered $ unset _autoenv_stack_entered
$ rm $ENV_AUTHORIZATION_FILE $ rm $AUTOENV_ENV_FILENAME
$ _dotenv_read_answer() { echo 'n' } $ _autoenv_ask_for_yes() { echo "no"; return 1 }
$ cd . $ 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)
********************************************** **********************************************
@ -74,16 +69,14 @@ Now, will it take no for an answer?
********************************************** **********************************************
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 didn't add it.
Lets also try one more time to ensure it didnt add it
$ cd . $ 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)
********************************************** **********************************************
@ -91,4 +84,4 @@ Lets also try one more time to ensure it didnt add it
********************************************** **********************************************
Would you like to authorize it? [y/N] Would you like to authorize it? (type 'yes') no

View File

@ -1,29 +1,26 @@
Test $PWD and $_dotenv_cwd. Test $PWD, $_autoenv_from_dir and _autoenv_to_dir.
Ensure we have our mocked out ENV_AUTHORIZATION_FILE. $ source $TESTDIR/setup.sh
$ [[ $ENV_AUTHORIZATION_FILE[0,4] == '/tmp' ]] || return 1
Setup env actions / output. Setup env actions / output.
$ DOTENV_LOOK_UPWARDS=1 $ AUTOENV_LOOK_UPWARDS=1
$ mkdir -p sub/sub2 $ mkdir -p sub/sub2
$ cd sub $ cd sub
$ echo 'echo ENTERED: cwd:${PWD:t} ${_dotenv_cwd:t}' >> .env $ echo 'echo ENTERED: PWD:${PWD:t} from:${_autoenv_from_dir:t} to:${_autoenv_to_dir:t}' > .env
$ echo 'echo LEFT: cwd:${PWD:t} ${_dotenv_cwd:t}' >> .env.leave $ echo 'echo LEFT: PWD:${PWD:t} from:${_autoenv_from_dir:t} to:${_autoenv_to_dir:t}' > .env.leave
Manually create auth files. Manually create auth files.
$ echo "$PWD/$DOTENV_FILE_ENTER:$(echo $(<$DOTENV_FILE_ENTER) | shasum)" > $ENV_AUTHORIZATION_FILE $ test_autoenv_auth_env_files
$ echo "$PWD/$DOTENV_FILE_LEAVE:$(echo $(<$DOTENV_FILE_LEAVE) | shasum)" >> $ENV_AUTHORIZATION_FILE
The actual tests. The actual tests.
$ cd . $ cd .
ENTERED: cwd:sub sub ENTERED: PWD:sub from:sub to:sub
$ cd .. $ cd ..
LEFT: cwd:sub cwd.t LEFT: PWD:sub from:sub to:cwd.t
$ cd sub/sub2 $ cd sub/sub2
ENTERED: cwd:sub sub2 ENTERED: PWD:sub from:cwd.t to:sub2

View File

@ -1,7 +1,4 @@
Ensure we have our mocked out ENV_AUTHORIZATION_FILE $ source $TESTDIR/setup.sh
$ [[ $ENV_AUTHORIZATION_FILE[0,4] == '/tmp' ]] || return 1
Lets set a simple .env action Lets set a simple .env action
@ -12,9 +9,10 @@ Lets set a simple .env action
Change to the directory. Change to the directory.
$ _dotenv_read_answer() { echo 'y' } $ _autoenv_ask_for_yes() { echo "yes"; return 0 }
$ cd . $ 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)
********************************************** **********************************************
@ -22,15 +20,16 @@ Change to the directory.
********************************************** **********************************************
Would you like to authorize it? [y/N] Would you like to authorize it? (type 'yes') yes
ENTERED ENTERED
Leave the directory and answer "no". Leave the directory and answer "no".
$ _dotenv_read_answer() { echo 'n' } $ _autoenv_ask_for_yes() { echo "no"; return 1 }
$ cd .. $ 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)
********************************************** **********************************************
@ -38,14 +37,15 @@ Leave the directory and answer "no".
********************************************** **********************************************
Would you like to authorize it? [y/N] Would you like to authorize it? (type 'yes') no
$ cd sub $ cd sub
ENTERED ENTERED
$ _dotenv_read_answer() { echo 'y' } $ _autoenv_ask_for_yes() { echo "yes"; return 0 }
$ cd .. $ 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)
********************************************** **********************************************
@ -53,13 +53,13 @@ Leave the directory and answer "no".
********************************************** **********************************************
Would you like to authorize it? [y/N] Would you like to authorize it? (type 'yes') yes
LEFT LEFT
Now check with subdirs, looking upwards. Now check with subdirs, looking upwards.
$ DOTENV_LOOK_UPWARDS=1 $ AUTOENV_LOOK_UPWARDS=1
$ mkdir sub/child $ mkdir sub/child
$ cd sub/child $ cd sub/child
ENTERED ENTERED
@ -71,7 +71,7 @@ Now check with subdirs, looking upwards.
Now check with subdirs, not looking at parent dirs. Now check with subdirs, not looking at parent dirs.
$ DOTENV_LOOK_UPWARDS=0 $ AUTOENV_LOOK_UPWARDS=0
$ cd sub/child $ cd sub/child
$ cd .. $ cd ..
ENTERED ENTERED
@ -80,10 +80,10 @@ Now check with subdirs, not looking at parent dirs.
LEFT 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 $ unset _autoenv_stack_entered
$ DOTENV_HANDLE_LEAVE=0 $ AUTOENV_HANDLE_LEAVE=0
$ cd sub $ cd sub
ENTERED ENTERED
$ cd .. $ cd ..

167
tests/recurse-upwards.t Normal file
View File

@ -0,0 +1,167 @@
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

18
tests/setup.sh Normal file
View File

@ -0,0 +1,18 @@
# 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
}

32
tests/varstash.t Normal file
View File

@ -0,0 +1,32 @@
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