226 lines
6.5 KiB
Bash
Executable File
226 lines
6.5 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
|
|
# pass extension for managing .env files
|
|
|
|
VERSION="0.1.0"
|
|
export PASSWORD_STORE_DIR="${PASSWORD_STORE_DIR:-$HOME/.password-store}"
|
|
|
|
# Robust gpg-agent environment setup
|
|
if [ -n "${GPG_AGENT_INFO}" ]; then
|
|
# gpg-agent is already running and GPG_AGENT_INFO is set
|
|
export GPG_AGENT_INFO
|
|
elif pgrep -x gpg-agent >/dev/null; then
|
|
# gpg-agent is running but GPG_AGENT_INFO is not set, try to find it
|
|
if [ -f "${HOME}/.gnupg/gpg-agent-info" ]; then
|
|
. "${HOME}/.gnupg/gpg-agent-info"
|
|
export GPG_AGENT_INFO
|
|
fi
|
|
else
|
|
# gpg-agent is not running, start it
|
|
eval $(gpg-agent --daemon)
|
|
fi
|
|
|
|
# Ensure GPG_TTY is set for non-interactive shells
|
|
export GPG_TTY=$(tty)
|
|
|
|
# Helper functions
|
|
die() {
|
|
echo "Error: $*" >&2
|
|
exit 1
|
|
}
|
|
|
|
yesno() {
|
|
local answer
|
|
read -r -p "$1 [y/N] " answer
|
|
[[ "$answer" =~ [Yy] ]]
|
|
}
|
|
|
|
# Import .env file into pass
|
|
cmd_import() {
|
|
local env_file="${1:-.env}"
|
|
[[ -f "$env_file" ]] || die "Environment file not found: $env_file"
|
|
|
|
local project_name
|
|
project_name=$(basename "$PWD")
|
|
echo "Importing environment variables for project: $project_name"
|
|
|
|
while IFS= read -r line || [[ -n "$line" ]]; do
|
|
# Trim leading whitespace from the whole line first
|
|
line="${line#"${line%%[![:space:]]*}"}"
|
|
|
|
# Skip empty lines and comment-only lines
|
|
if [[ -z "$line" ]] || [[ "$line" =~ ^# ]]; then
|
|
continue
|
|
fi
|
|
|
|
local comment=""
|
|
local key_value_part="$line"
|
|
|
|
# Check for inline comment
|
|
if [[ "$line" == *"#"* ]]; then
|
|
key_value_part="${line%%#*}"
|
|
comment="${line#*#}"
|
|
fi
|
|
|
|
# Split on the first '='
|
|
local key="${key_value_part%%=*}"
|
|
local value="${key_value_part#*=}"
|
|
|
|
# Trim whitespace from key and value
|
|
key="${key#"${key%%[![:space:]]*}"}"
|
|
key="${key%"${key##*[![:space:]]}"}"
|
|
value="${value#"${value%%[![:space:]]*}"}"
|
|
value="${value%"${value##*[![:space:]]}"}"
|
|
|
|
# Check for *file* tag in comment
|
|
if [[ "$comment" == *"*file*"* ]]; then
|
|
# The value is a file path. Let's remove quotes around it if any.
|
|
if [[ "$value" =~ ^'(.*)'$ ]]; then
|
|
value="${BASH_REMATCH[1]}"
|
|
elif [[ "$value" =~ ^"(.*)"$ ]]; then
|
|
value="${BASH_REMATCH[1]}"
|
|
fi
|
|
|
|
local file_path="$value"
|
|
if [[ ! -f "$file_path" ]]; then
|
|
local env_dir
|
|
env_dir=$(dirname "$env_file")
|
|
if [[ -f "$env_dir/$file_path" ]]; then
|
|
file_path="$env_dir/$file_path"
|
|
else
|
|
echo "Warning: File not found for $key: $value. Skipping." >&2
|
|
continue
|
|
fi
|
|
fi
|
|
echo "Reading value for $key from file: $file_path"
|
|
local stored_path
|
|
local project_root
|
|
project_root=$(realpath "$PWD")
|
|
local abs_file_path
|
|
abs_file_path=$(realpath "$file_path")
|
|
|
|
# Check if the file is within the project directory
|
|
if [[ "$abs_file_path" == "$project_root"* ]]; then
|
|
# Store the relative path
|
|
stored_path="${abs_file_path#$project_root/}"
|
|
else
|
|
# Store only the filename, preserving full name
|
|
stored_path="${abs_file_path##*/}"
|
|
fi
|
|
echo "Debug: Storing path: '$stored_path'" >&2
|
|
|
|
local file_content
|
|
file_content=$(<"$file_path")
|
|
value="# dotenv-file-path: $stored_path
|
|
$file_content"
|
|
else
|
|
# It's a regular value, remove quotes if they are there.
|
|
if [[ "$value" =~ ^'(.*)'$ ]]; then
|
|
value="${BASH_REMATCH[1]}"
|
|
elif [[ "$value" =~ ^"(.*)"$ ]]; then
|
|
value="${BASH_REMATCH[1]}"
|
|
fi
|
|
fi
|
|
|
|
local store_path="dotenv/$project_name/$key"
|
|
echo "Importing $key to $store_path"
|
|
printf "%s" "$value" | pass insert --multiline "$store_path" >/dev/null || die "Failed to insert $key"
|
|
done < "$env_file"
|
|
|
|
echo "Import complete."
|
|
}
|
|
|
|
|
|
# Export .env file from pass
|
|
cmd_export() {
|
|
local project_name="${1:-$(basename "$PWD")}"
|
|
local env_file=".env"
|
|
|
|
echo "Exporting environment variables for project: $project_name"
|
|
|
|
local project_path="dotenv/$project_name"
|
|
local project_store_path="$PASSWORD_STORE_DIR/$project_path"
|
|
|
|
if [[ ! -d "$project_store_path" ]]; then
|
|
die "No environment variables found for project: $project_name"
|
|
fi
|
|
|
|
if [[ -f "$env_file" ]] && ! yesno "Overwrite existing $env_file?"; then
|
|
die "Export aborted."
|
|
fi
|
|
|
|
# Clear the file
|
|
> "$env_file"
|
|
|
|
# Use find to get a reliable, machine-readable list of secret files.
|
|
find "$project_store_path" -type f -name "*.gpg" | while read -r gpg_file; do
|
|
# Derive the secret name from the gpg file path
|
|
local entry
|
|
entry="${gpg_file#$PASSWORD_STORE_DIR/}"
|
|
entry="${entry%.gpg}"
|
|
|
|
local key
|
|
key=$(basename "$entry")
|
|
|
|
local full_content
|
|
full_content=$(/usr/bin/pass show "$entry")
|
|
|
|
# Extract the header line and then the stored_path using sed
|
|
local header_line
|
|
header_line=$(echo "$full_content" | head -n 1)
|
|
|
|
if [[ "$header_line" =~ ^#\ dotenv-file-path:\ (.*) ]]; then
|
|
local stored_path="${BASH_REMATCH[1]}"
|
|
local file_content
|
|
file_content=$(echo "$full_content" | tail -n +2)
|
|
|
|
local new_filepath="$stored_path"
|
|
|
|
# Ensure the directory exists
|
|
local new_file_dir
|
|
new_file_dir=$(dirname "$new_filepath")
|
|
if [[ ! -d "$new_file_dir" ]]; then
|
|
mkdir -p "$new_file_dir"
|
|
fi
|
|
|
|
echo "Exporting $key to file: $new_filepath"
|
|
printf "%s" "$file_content" > "$new_filepath"
|
|
|
|
echo "$key=$new_filepath # *file*" >> "$env_file"
|
|
else
|
|
echo "Exporting $key"
|
|
echo "$key=$full_content" >> "$env_file"
|
|
fi
|
|
done
|
|
|
|
echo "Export complete. See $env_file"
|
|
}
|
|
|
|
# Show help
|
|
cmd_help() {
|
|
cat <<-_EOF
|
|
Usage: pass dotenv import|export [project_name]
|
|
|
|
Commands:
|
|
import - Import a .env file into the password store.
|
|
export - Export a .env file from the password store.
|
|
_EOF
|
|
}
|
|
|
|
|
|
# Main command handler
|
|
case "$1" in
|
|
import)
|
|
shift
|
|
cmd_import "$@"
|
|
;;
|
|
export)
|
|
shift
|
|
cmd_export "$@"
|
|
;;
|
|
-h|--help|help)
|
|
cmd_help
|
|
;;
|
|
*) die 'Usage: pass dotenv import|export [project_name]' ;;
|
|
esac
|