-
Notifications
You must be signed in to change notification settings - Fork 2.3k
new plugin: safe_autoenv #2354
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
oz123
wants to merge
1
commit into
Bash-it:master
Choose a base branch
from
oz123:safe_autoenv
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+225
−0
Open
new plugin: safe_autoenv #2354
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,225 @@ | ||
# shellcheck shell=bash | ||
# BASH_IT_LOAD_PRIORITY: 200 | ||
|
||
# Autoenv Plugin for Bash-it with automatic cleanup | ||
# Automatically loads .env files when changing directories | ||
# Automatically unsets variables when leaving directories | ||
|
||
cite about-plugin | ||
about-plugin 'Automatically loads .env files and cleans up when leaving directories' | ||
|
||
# Global tracking for autoenv state | ||
declare -A _AUTOENV_PROCESSED_DIRS # Track processed directories | ||
declare -A _AUTOENV_DIR_VARS # Map directories to their variables | ||
declare -A _AUTOENV_VAR_SOURCES # Map variables to their source directories | ||
declare _AUTOENV_LAST_PWD="" # Track last working directory | ||
|
||
_autoenv_init() { | ||
typeset target home _file current_dir | ||
typeset -a _files | ||
target=$1 | ||
home="${HOME%/*}" | ||
|
||
_files=($( | ||
current_dir="$target" | ||
while [[ "$current_dir" != "/" && "$current_dir" != "$home" ]]; do | ||
_file="$current_dir/.env" | ||
if [[ -e "${_file}" ]]; then | ||
echo "${_file}" | ||
fi | ||
# Move to parent directory | ||
current_dir="$(dirname "$current_dir")" | ||
done | ||
)) | ||
|
||
# Process files in reverse order (from root to current directory) | ||
local _file_count=${#_files[@]} | ||
local i | ||
for ((i = _file_count - 1; i >= 0; i--)); do | ||
local env_file="${_files[i]}" | ||
local env_dir="$(dirname "$env_file")" | ||
Check warningCode scanning / shellcheck SC2155 Warning
Declare and assign separately to avoid masking return values.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I suggest you install the pre-commit in this repo, it will detect this kind of stuff before you push any commits unfixed. thanks! |
||
|
||
# Only process if this directory hasn't been processed yet | ||
# OR if it was processed but its variables were cleaned up | ||
if [[ -z "${_AUTOENV_PROCESSED_DIRS[$env_dir]}" ]] || [[ -z "${_AUTOENV_DIR_VARS[$env_dir]}" ]]; then | ||
echo "Processing $env_file" | ||
_autoenv_process_file "$env_file" "$env_dir" | ||
_AUTOENV_PROCESSED_DIRS[$env_dir]=1 | ||
fi | ||
done | ||
} | ||
|
||
_autoenv_process_file() { | ||
local env_file="$1" | ||
local env_dir="$2" | ||
local line key value original_line line_number=0 | ||
local -a dir_vars=() | ||
|
||
# Check if file is readable | ||
if [[ ! -r "$env_file" ]]; then | ||
echo "Warning: Cannot read $env_file" >&2 | ||
return 1 | ||
fi | ||
|
||
# Read the file line by line | ||
while IFS= read -r line || [[ -n "$line" ]]; do | ||
((line_number++)) | ||
original_line="$line" | ||
|
||
# Skip empty lines and comments | ||
[[ -z "$line" || "$line" =~ ^[[:space:]]*# ]] && continue | ||
|
||
# Remove leading whitespace | ||
line="${line#"${line%%[![:space:]]*}"}" | ||
|
||
# Check if line matches KEY=value pattern | ||
if [[ "$line" =~ ^([A-Za-z_][A-Za-z0-9_]*)=(.*)$ ]]; then | ||
key="${BASH_REMATCH[1]}" | ||
value="${BASH_REMATCH[2]}" | ||
|
||
# Validate key name (additional safety check) | ||
if [[ ! "$key" =~ ^[A-Za-z_][A-Za-z0-9_]*$ ]]; then | ||
echo "Warning: Invalid variable name '$key' at line $line_number in $env_file" >&2 | ||
continue | ||
fi | ||
|
||
# Handle quoted values (remove outer quotes if present) | ||
if [[ "$value" =~ ^\"(.*)\"$ ]]; then | ||
value="${BASH_REMATCH[1]}" | ||
elif [[ "$value" =~ ^\'(.*)\'$ ]]; then | ||
value="${BASH_REMATCH[1]}" | ||
fi | ||
|
||
# Export the variable silently | ||
export "$key=$value" | ||
|
||
# Track the variable | ||
dir_vars+=("$key") | ||
_AUTOENV_VAR_SOURCES["$key"]="$env_dir" | ||
|
||
else | ||
echo "Warning: Skipping invalid line $line_number in $env_file: $original_line" >&2 | ||
fi | ||
done < "$env_file" | ||
|
||
# Store variables for this directory | ||
if [[ ${#dir_vars[@]} -gt 0 ]]; then | ||
_AUTOENV_DIR_VARS["$env_dir"]="${dir_vars[*]}" | ||
fi | ||
} | ||
|
||
# Check if a directory is an ancestor of the current directory | ||
_autoenv_is_ancestor() { | ||
local ancestor="$1" | ||
local current="$PWD" | ||
|
||
# Normalize paths (remove trailing slashes) | ||
ancestor="${ancestor%/}" | ||
current="${current%/}" | ||
|
||
# Check if current path starts with ancestor path | ||
[[ "$current" == "$ancestor"* ]] | ||
} | ||
|
||
# Clean up variables from directories we've left | ||
_autoenv_cleanup() { | ||
local dir var_list var | ||
|
||
# Check each directory we've processed | ||
for dir in "${!_AUTOENV_DIR_VARS[@]}"; do | ||
# If this directory is no longer an ancestor of current directory | ||
if ! _autoenv_is_ancestor "$dir"; then | ||
var_list="${_AUTOENV_DIR_VARS[$dir]}" | ||
|
||
# Unset each variable from this directory | ||
for var in $var_list; do | ||
if [[ "${_AUTOENV_VAR_SOURCES[$var]}" == "$dir" ]]; then | ||
echo "Unsetting $var (from $dir)" | ||
unset "$var" | ||
unset "_AUTOENV_VAR_SOURCES[$var]" | ||
fi | ||
done | ||
|
||
# Remove directory from tracking AND clear its processed status | ||
unset "_AUTOENV_DIR_VARS[$dir]" | ||
unset "_AUTOENV_PROCESSED_DIRS[$dir]" | ||
fi | ||
done | ||
} | ||
|
||
# Main prompt command function | ||
_autoenv_prompt_command() { | ||
local current_dir="$PWD" | ||
|
||
# If directory changed, perform cleanup first | ||
if [[ "$current_dir" != "$_AUTOENV_LAST_PWD" ]]; then | ||
_autoenv_cleanup | ||
_AUTOENV_LAST_PWD="$current_dir" | ||
fi | ||
|
||
# Always try to initialize the current directory and its ancestors | ||
# The _autoenv_init function will handle checking if processing is needed | ||
_autoenv_init "$current_dir" | ||
} | ||
|
||
# Public function for manual use | ||
autoenv() { | ||
case "$1" in | ||
"reload"|"refresh") | ||
# Clear all tracking and reload | ||
_autoenv_clear_all | ||
_autoenv_init "$PWD" | ||
;; | ||
"status") | ||
echo "Processed directories:" | ||
for dir in "${!_AUTOENV_PROCESSED_DIRS[@]}"; do | ||
echo " $dir" | ||
done | ||
echo | ||
echo "Active variables by directory:" | ||
for dir in "${!_AUTOENV_DIR_VARS[@]}"; do | ||
echo " $dir: ${_AUTOENV_DIR_VARS[$dir]}" | ||
done | ||
echo | ||
echo "Variable sources:" | ||
for var in "${!_AUTOENV_VAR_SOURCES[@]}"; do | ||
echo " $var -> ${_AUTOENV_VAR_SOURCES[$var]}" | ||
done | ||
;; | ||
"clean"|"cleanup") | ||
_autoenv_cleanup | ||
;; | ||
"clear") | ||
_autoenv_clear_all | ||
;; | ||
*) | ||
_autoenv_init "${1:-$PWD}" | ||
;; | ||
esac | ||
} | ||
|
||
# Clear all autoenv state and unset tracked variables | ||
_autoenv_clear_all() { | ||
local var | ||
|
||
# Unset all tracked variables | ||
for var in "${!_AUTOENV_VAR_SOURCES[@]}"; do | ||
echo "Clearing $var" | ||
unset "$var" | ||
done | ||
|
||
# Clear all tracking arrays | ||
unset _AUTOENV_PROCESSED_DIRS | ||
unset _AUTOENV_DIR_VARS | ||
unset _AUTOENV_VAR_SOURCES | ||
|
||
# Reinitialize arrays | ||
declare -A _AUTOENV_PROCESSED_DIRS | ||
declare -A _AUTOENV_DIR_VARS | ||
declare -A _AUTOENV_VAR_SOURCES | ||
|
||
_AUTOENV_LAST_PWD="" | ||
} | ||
|
||
# Hook into bash-it's prompt command system | ||
safe_append_prompt_command '_autoenv_prompt_command' |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Check warning
Code scanning / shellcheck
SC2207 Warning