Created
May 15, 2024 18:51
-
-
Save mrjk/48e6d1dca90263e0d011b829e1410705 to your computer and use it in GitHub Desktop.
Simple shell script that generate env vars from files
This file contains 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
#!/bin/bash | |
# | |
# Read one or more dotfiles, and load | |
# | |
# Author: mrjk | |
set -euo pipefail | |
_log () | |
{ | |
#return 0 | |
>&2 echo "$@" | |
} | |
fn_update () | |
{ | |
local kind=$1 | |
shift 1 | |
case "$kind" in | |
env) | |
local name=$1 | |
shift 1 | |
_log "INFO: Update from env: $name" | |
STORED_VARS_ENV["$name"]="$@" | |
;; | |
file) | |
local file=$1 | |
local name=$2 | |
shift 2 | |
if $DOTENV_FIRST_ONLY; then | |
# Ensure destination vars does not exists | |
if [[ -v STORED_VARS_FILES["$name"] ]]; then | |
_log "INFO: Skip already defined var '$name' from $file" | |
return 0 | |
fi | |
fi | |
_log "INFO: Update from file '$file': $name" | |
STORED_VARS_FILES["$name"]="$@" | |
;; | |
*) | |
echo "Unsupported: $kind" | |
return 1 | |
;; | |
esac | |
} | |
get_var_names () | |
{ | |
local file=$1 | |
sed -En '/^[a-zA-Z0-9_]+=.*/s/([^=]*)=.*/\1/p' "$file" | |
} | |
show_env_vars () | |
{ | |
local prefix="env_" | |
for vname in ${VAR_NAMES[@]}; do | |
# Skip if known vars is not declared | |
[[ -v "$vname" ]] || continue | |
# Display loading function | |
local value=${!vname} | |
echo "fn_update env '$vname' \"$value\"" | |
done | |
} | |
show_file_vars () | |
{ | |
local file=$1 | |
local prefix=${2:-local_} | |
( | |
# Clear environment | |
unset ${VAR_NAMES[@]} | |
# Parse file without bash | |
eval "$(cat $file)" | |
for vname in ${VAR_NAMES[@]}; do | |
# Skip if known vars is not declared | |
if ! [[ -v "$vname" ]]; then | |
#_log INFO "Skip missing var: $vname in $file" | |
continue | |
else | |
_log DEBUG "Found var '$vname' in $file" | |
fi | |
# Display loading function | |
local value="${!vname}" | |
echo "fn_update file '$file' '$vname' \"$value\"" | |
#if [[ -n "${value}" ]]; then | |
# #echo "${prefix}$vname=\"$value\"" | |
# #echo "STORED_VARS['$vname']=\"$value\"" | |
# #STORED_VARS["$vname"]="$value" | |
#fi | |
done | |
) | |
} | |
show_vals () | |
{ | |
local files=${@:-$DOTENV_ENV_FILES} | |
# Check missing files | |
local _files= | |
for file in $files; do | |
if [[ -f "$file" ]] ; then | |
_files="${_files} $file" | |
else | |
if ! $DOTENV_IGNORE_MISSING_FILES; then | |
>&2 echo "ERROR: Missing file: $file" | |
return 1 | |
else | |
_log DEBUG "Ignore missing file: $file" | |
continue | |
fi | |
fi | |
done | |
files=$_files | |
# Build known vars list | |
local VAR_NAMES=() | |
for file in $files; do | |
for name in $(get_var_names "$file"); do | |
VAR_NAMES+=( "$name" ) | |
done | |
done | |
_log "INFO: Var names: ${VAR_NAMES[@]}" | |
declare -A STORED_VARS_ENV=() | |
declare -A STORED_VARS_FILES=() | |
# Parse each files | |
arrays_code=$( | |
for file in $files; do | |
echo "# Parse file: $file" | |
show_file_vars $file local_ | |
done | |
show_env_vars | |
) | |
# Process var definitions | |
eval "$arrays_code" | |
# Process var merging | |
declare -A FINAL_ENV=() | |
# Insert file variables | |
for name in "${!STORED_VARS_FILES[@]}"; do | |
#_log "INFO: var: ${name}: ${STORED_VARS_FILES[$name]}" | |
FINAL_ENV["$name"]=${STORED_VARS_FILES[$name]} | |
done | |
# Override environment vars | |
if $DOTENV_ENV_OVERRIDE; then | |
for name in "${!STORED_VARS_ENV[@]}"; do | |
#_log "INFO: env: ${name}: ${STORED_VARS_ENV[$name]}" | |
FINAL_ENV["$name"]=${STORED_VARS_ENV[$name]} | |
done | |
fi | |
# Generate output | |
>&2 echo 'INFO: Generated bash code:' | |
local prefix='' | |
local snames="${!FINAL_ENV[@]}" | |
if $DOTENV_SORTED; then | |
# Sort vars by name | |
snames=$( tr ' ' '\n' <<< "$snames" | sort ) | |
fi | |
if $DOTENV_EXPORT; then | |
prefix="export " | |
fi | |
for name in $snames ; do | |
echo "${prefix}${name}=\"${FINAL_ENV[$name]}\"" | |
done | |
} | |
show_usage () | |
{ | |
cat <<EOF | |
Simple tool to fetch environment variables from files | |
Usage: | |
$0 [show|run] | |
EOF | |
} | |
main () | |
{ | |
local cmd=$1 | |
shift 1 | |
# Set to true to enable alphabetical sort | |
DOTENV_SORTED=${DOTENV_SORTED:-true} | |
# Set to true to prefix output with export. Useful for sourcing shell output | |
DOTENV_EXPORT=${DOTENV_EXPORT:-false} | |
# Default env files to load. Missing files are ignored silentely | |
DOTENV_ENV_FILES=${DOTENV_ENV_FILES:-.env .env.secrets .env.local} | |
# Ignore missing files | |
DOTENV_IGNORE_MISSING_FILES=${DOTENV_IGNORE_MISSING_FILES:-true} | |
# Environment vars act as overrides | |
DOTENV_ENV_OVERRIDE=${DOTENV_ENV_OVERRIDE:-true} | |
# Set to true to only consider the first found variable | |
DOTENV_FIRST_ONLY=${DOTENV_FIRST_ONLY:-false} | |
# Process command | |
case "$cmd" in | |
show) | |
show_vals $@ | |
;; | |
run) | |
eval "$(DOTENV_EXPORT=true show_vals)" | |
exec "$@" | |
;; | |
help|--help|-h) | |
show_usage | |
;; | |
*) | |
echo "Command not supported: $cmd" | |
return 1 | |
;; | |
esac | |
} | |
main $@ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment