diff --git a/lib/jeannie/env.sh b/lib/jeannie/env.sh index 45e4533..b855bc9 100644 --- a/lib/jeannie/env.sh +++ b/lib/jeannie/env.sh @@ -189,6 +189,84 @@ sops_available() { command -v sops >/dev/null 2>&1 } +sops_load_secrets() { + local file="$1" + local key_file + local decrypted_text + local rendered_env + + if [[ ! -s "${file}" ]]; then + echo "Missing encrypted secrets file: ${file}" >&2 + return 1 + fi + if ! sops_available; then + echo "sops is required to decrypt ${file}." >&2 + return 1 + fi + + key_file="$(sops_age_key_file)" + if [[ -s "${key_file}" ]]; then + decrypted_text="$(SOPS_AGE_KEY_FILE="${key_file}" sops -d "${file}")" || return 1 + else + decrypted_text="$(sops -d "${file}")" || return 1 + fi + + rendered_env="$(SECRET_TEXT="${decrypted_text}" python3 - <<'PY' +import os +import re +import shlex + +try: + import yaml +except Exception: + yaml = None + +text = os.environ.get("SECRET_TEXT", "") +if yaml is not None: + document = yaml.safe_load(text) or {} + if not isinstance(document, dict): + document = {} +else: + document = {} + pattern = re.compile(r"^([A-Za-z_][A-Za-z0-9_]*):\s*(.*)$") + for line in text.splitlines(): + match = pattern.match(line) + if not match: + continue + key, value = match.groups() + if key == "sops": + continue + document[key] = value.strip().strip('"').strip("'") + +for key, value in document.items(): + if key == "sops" or not re.match(r"^[A-Za-z_][A-Za-z0-9_]*$", str(key)): + continue + if value is None: + value = "" + if isinstance(value, (dict, list)): + continue + print(f"export {key}={shlex.quote(str(value))}") +PY +)" + + if [[ -z "${rendered_env}" ]]; then + echo "No exportable secrets found in ${file}." >&2 + return 1 + fi + + while IFS= read -r assignment; do + local name + + [[ -n "${assignment}" ]] || continue + name="${assignment#export }" + name="${name%%=*}" + if [[ -n "${!name:-}" ]]; then + continue + fi + eval "${assignment}" + done <<<"${rendered_env}" +} + secrets_init() { local key_file local key_dir @@ -310,4 +388,3 @@ secrets_check() { echo "SOPS config checks passed for ${found_files} encrypted secret file(s)." fi } -