mirror of
https://github.com/alexbelgium/hassio-addons.git
synced 2026-05-30 20:34:04 +02:00
Enhance global_var.sh with improved logging and injection
Updated logging messages and improved script structure for clarity. Added support for injecting environment variables into bash configuration files.
This commit is contained in:
@@ -3,24 +3,41 @@
|
|||||||
|
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# Guard: only run inside Supervisor-managed add-ons
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
if ! bashio::supervisor.ping 2>/dev/null; then
|
if ! bashio::supervisor.ping 2>/dev/null; then
|
||||||
echo "..."
|
echo "..."
|
||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
bashio::log.notice "This script converts all addon options to environment variables. Custom variables can be set using env_vars."
|
bashio::log.notice "This script converts add-on options (options.json) into environment variables."
|
||||||
|
bashio::log.notice "Custom variables can be set using the env_vars option."
|
||||||
bashio::log.notice "Additional informations : https://github.com/alexbelgium/hassio-addons/wiki/Add-Environment-variables-to-your-Addon-2"
|
bashio::log.notice "Additional informations : https://github.com/alexbelgium/hassio-addons/wiki/Add-Environment-variables-to-your-Addon-2"
|
||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# Inputs / outputs
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
JSONSOURCE="/data/options.json"
|
JSONSOURCE="/data/options.json"
|
||||||
ENV_FILE="/.env"
|
ENV_FILE="/.env"
|
||||||
|
ETC_ENV_FILE="/etc/environment"
|
||||||
|
|
||||||
if [[ ! -f "$ENV_FILE" ]]; then
|
if [[ ! -f "$JSONSOURCE" ]]; then
|
||||||
printf '# Generated by 00-global_var.sh from %s\n' "$JSONSOURCE" > "$ENV_FILE"
|
bashio::exit.nok "Missing ${JSONSOURCE}"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# mktemp
|
if ! command -v jq >/dev/null 2>&1; then
|
||||||
|
bashio::exit.nok "jq is required but not found"
|
||||||
|
fi
|
||||||
|
|
||||||
|
mkdir -p /etc
|
||||||
|
touch "$ETC_ENV_FILE"
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# mktemp helper (safe temp file creation)
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
mktemp_safe() {
|
mktemp_safe() {
|
||||||
local tmpdir="${TMPDIR:-/tmp}"
|
local tmpdir="${TMPDIR:-/tmp}"
|
||||||
mkdir -p "$tmpdir" || return 1
|
mkdir -p "$tmpdir" || return 1
|
||||||
@@ -30,7 +47,10 @@ mktemp_safe() {
|
|||||||
printf '%s\n' "$tmpfile"
|
printf '%s\n' "$tmpfile"
|
||||||
}
|
}
|
||||||
|
|
||||||
# Define secrets location (optional)
|
# -----------------------------------------------------------------------------
|
||||||
|
# Secrets support:
|
||||||
|
# - If an option value is "!secret foo", try to resolve it from secrets.yaml
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
SECRETSOURCE=""
|
SECRETSOURCE=""
|
||||||
if [[ -f /homeassistant/secrets.yaml ]]; then
|
if [[ -f /homeassistant/secrets.yaml ]]; then
|
||||||
SECRETSOURCE="/homeassistant/secrets.yaml"
|
SECRETSOURCE="/homeassistant/secrets.yaml"
|
||||||
@@ -38,12 +58,20 @@ elif [[ -f /config/secrets.yaml ]]; then
|
|||||||
SECRETSOURCE="/config/secrets.yaml"
|
SECRETSOURCE="/config/secrets.yaml"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Injection block markers (single block, idempotent)
|
# -----------------------------------------------------------------------------
|
||||||
|
# Injection block markers:
|
||||||
|
# 1) EXPORT_BLOCK_*: injected into run scripts / bashrc so services inherit vars
|
||||||
|
# 2) FILE_BLOCK_* : written into /.env and /etc/environment (idempotent)
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
BLOCK_BEGIN="# --- BEGIN ADDON ENV (generated) ---"
|
BLOCK_BEGIN="# --- BEGIN ADDON ENV (generated) ---"
|
||||||
BLOCK_END="# --- END ADDON ENV (generated) ---"
|
BLOCK_END="# --- END ADDON ENV (generated) ---"
|
||||||
|
|
||||||
|
FILE_BLOCK_BEGIN="# --- BEGIN ADDON ENV FILE (generated) ---"
|
||||||
|
FILE_BLOCK_END="# --- END ADDON ENV FILE (generated) ---"
|
||||||
|
|
||||||
EXPORT_BLOCK_FILE="$(mktemp_safe)"
|
EXPORT_BLOCK_FILE="$(mktemp_safe)"
|
||||||
trap 'rm -f "$EXPORT_BLOCK_FILE"' EXIT
|
ENV_KV_FILE="$(mktemp_safe)"
|
||||||
|
trap 'rm -f "$EXPORT_BLOCK_FILE" "$ENV_KV_FILE"' EXIT
|
||||||
|
|
||||||
{
|
{
|
||||||
echo "${BLOCK_BEGIN}"
|
echo "${BLOCK_BEGIN}"
|
||||||
@@ -51,7 +79,9 @@ trap 'rm -f "$EXPORT_BLOCK_FILE"' EXIT
|
|||||||
echo "${BLOCK_END}"
|
echo "${BLOCK_END}"
|
||||||
} > "${EXPORT_BLOCK_FILE}"
|
} > "${EXPORT_BLOCK_FILE}"
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
# Protected variables that should not be overwritten
|
# Protected variables that should not be overwritten
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
declare -A PROTECTED_VARS=(
|
declare -A PROTECTED_VARS=(
|
||||||
["PATH"]=1
|
["PATH"]=1
|
||||||
["HOME"]=1
|
["HOME"]=1
|
||||||
@@ -133,6 +163,8 @@ resolve_secret_if_needed() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
append_export_line_for_injection() {
|
append_export_line_for_injection() {
|
||||||
|
# Insert one export line right before BLOCK_END in EXPORT_BLOCK_FILE.
|
||||||
|
# (kept as-is to preserve behavior; safe and idempotent block replacement)
|
||||||
local key="$1"
|
local key="$1"
|
||||||
local value="$2"
|
local value="$2"
|
||||||
local quoted
|
local quoted
|
||||||
@@ -156,6 +188,9 @@ is_shell_run_script() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
inject_block_into_file() {
|
inject_block_into_file() {
|
||||||
|
# Inject/replace the generated export block in a target script:
|
||||||
|
# - If the block exists: replace it.
|
||||||
|
# - If not: insert it right after the shebang (if present), else at top.
|
||||||
local file="$1"
|
local file="$1"
|
||||||
local tmp
|
local tmp
|
||||||
tmp="$(mktemp_safe)"
|
tmp="$(mktemp_safe)"
|
||||||
@@ -198,12 +233,13 @@ inject_block_into_file() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
update_scripts_with_block() {
|
update_scripts_with_block() {
|
||||||
|
# Inject the export block into common service scripts and entrypoints.
|
||||||
local f
|
local f
|
||||||
local -A seen=()
|
local -A seen=()
|
||||||
|
|
||||||
shopt -s nullglob
|
shopt -s nullglob
|
||||||
|
|
||||||
# Added /etc/s6-overlay/s6-rc.d/*/run for newer S6 implementation (optional)
|
# Includes legacy and newer s6 locations
|
||||||
for f in /etc/services.d/*/run /etc/services.d/*/*run* /etc/cont-init.d/*.sh /etc/s6-overlay/s6-rc.d/*/run /*/entrypoint.sh /entrypoint.sh; do
|
for f in /etc/services.d/*/run /etc/services.d/*/*run* /etc/cont-init.d/*.sh /etc/s6-overlay/s6-rc.d/*/run /*/entrypoint.sh /entrypoint.sh; do
|
||||||
[[ -f "$f" ]] || continue
|
[[ -f "$f" ]] || continue
|
||||||
[[ -n "${seen[$f]:-}" ]] && continue
|
[[ -n "${seen[$f]:-}" ]] && continue
|
||||||
@@ -220,7 +256,53 @@ update_scripts_with_block() {
|
|||||||
shopt -u nullglob
|
shopt -u nullglob
|
||||||
}
|
}
|
||||||
|
|
||||||
|
replace_block_in_file() {
|
||||||
|
# Replace (or add) a generated block inside a plain text file.
|
||||||
|
# Used for /.env and /etc/environment to prevent infinite append growth.
|
||||||
|
local file="$1"
|
||||||
|
local begin="$2"
|
||||||
|
local end="$3"
|
||||||
|
local content_file="$4"
|
||||||
|
local tmp
|
||||||
|
|
||||||
|
tmp="$(mktemp_safe)"
|
||||||
|
|
||||||
|
if [[ ! -f "$file" ]]; then
|
||||||
|
touch "$file"
|
||||||
|
fi
|
||||||
|
|
||||||
|
awk -v bfile="$content_file" -v begin="$begin" -v end="$end" '
|
||||||
|
function print_block() {
|
||||||
|
while ((getline l < bfile) > 0) print l
|
||||||
|
close(bfile)
|
||||||
|
}
|
||||||
|
BEGIN { inblock=0; printed=0 }
|
||||||
|
{
|
||||||
|
if ($0 == begin) {
|
||||||
|
inblock=1
|
||||||
|
if (!printed) { print_block(); printed=1 }
|
||||||
|
next
|
||||||
|
}
|
||||||
|
if ($0 == end) { inblock=0; next }
|
||||||
|
if (inblock) next
|
||||||
|
print $0
|
||||||
|
}
|
||||||
|
END { if (!printed) print_block() }
|
||||||
|
' "$file" > "$tmp"
|
||||||
|
|
||||||
|
cat "$tmp" > "$file"
|
||||||
|
rm -f "$tmp"
|
||||||
|
}
|
||||||
|
|
||||||
export_option() {
|
export_option() {
|
||||||
|
# Core exporter:
|
||||||
|
# - validates key
|
||||||
|
# - resolves secrets
|
||||||
|
# - logs (redacted if sensitive unless verbose)
|
||||||
|
# - exports into current process
|
||||||
|
# - exports into s6 environment
|
||||||
|
# - queues key/value for generated /.env and /etc/environment blocks
|
||||||
|
# - adds export into injection block for scripts
|
||||||
local key="$1"
|
local key="$1"
|
||||||
local value="$2"
|
local value="$2"
|
||||||
|
|
||||||
@@ -244,75 +326,101 @@ export_option() {
|
|||||||
|
|
||||||
export "${key}=${value}"
|
export "${key}=${value}"
|
||||||
|
|
||||||
|
# Export for s6 services (preferred way)
|
||||||
if [[ -d /var/run/s6/container_environment ]]; then
|
if [[ -d /var/run/s6/container_environment ]]; then
|
||||||
printf '%s' "${value}" > "/var/run/s6/container_environment/${key}"
|
printf '%s' "${value}" > "/var/run/s6/container_environment/${key}"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo "${key}=$(dotenv_quote "$value")" >> "$ENV_FILE" 2>/dev/null || true
|
# Queue dotenv-style line for writing once (idempotent file update later)
|
||||||
mkdir -p /etc
|
echo "${key}=$(dotenv_quote "$value")" >> "$ENV_KV_FILE" 2>/dev/null || true
|
||||||
echo "${key}=$(dotenv_quote "$value")" >> /etc/environment 2>/dev/null || true
|
|
||||||
|
|
||||||
|
# Ensure scripts/services also see it (block injection)
|
||||||
append_export_line_for_injection "$key" "$value"
|
append_export_line_for_injection "$key" "$value"
|
||||||
}
|
}
|
||||||
|
|
||||||
mapfile -t arr < <(jq -r 'keys[]' "${JSONSOURCE}")
|
# -----------------------------------------------------------------------------
|
||||||
|
# One jq pass emits normalized key/value pairs:
|
||||||
|
# - exports all scalar top-level options (strings/numbers/bools)
|
||||||
|
# - skips objects/arrays (except env_vars)
|
||||||
|
# - supports env_vars formats:
|
||||||
|
# 1) [{name:"FOO", value:"bar"}, ...]
|
||||||
|
# 2) [{FOO:"bar", BAZ:"qux"}, ...]
|
||||||
|
# 3) ["FOO=bar", ...] (value may contain '=')
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
while IFS= read -r -d $'\0' key && IFS= read -r value; do
|
||||||
|
export_option "$key" "$value"
|
||||||
|
done < <(
|
||||||
|
jq -r '
|
||||||
|
def emit(k; v): "\((k|tostring))\u0000\((v|tostring))";
|
||||||
|
|
||||||
for KEYS in "${arr[@]}"; do
|
# --- 1) env_vars[] ---
|
||||||
jtype="$(jq -r --arg k "$KEYS" '.[$k] | type' "$JSONSOURCE")"
|
( .env_vars? // [] )[] as $ev
|
||||||
|
| if ($ev | type) == "object" then
|
||||||
|
if ($ev | has("name") and has("value")) then
|
||||||
|
emit($ev.name; ($ev.value // ""))
|
||||||
|
else
|
||||||
|
($ev | to_entries[] | emit(.key; (.value // "")))
|
||||||
|
end
|
||||||
|
else
|
||||||
|
($ev | tostring) as $s
|
||||||
|
| if ($s | test("^[^=]+=")) then
|
||||||
|
($s | capture("^(?<k>[^=]+)=(?<v>.*)$")) as $m
|
||||||
|
| emit($m.k; $m.v)
|
||||||
|
else empty end
|
||||||
|
end
|
||||||
|
,
|
||||||
|
# --- 2) top-level scalars excluding env_vars ---
|
||||||
|
to_entries[]
|
||||||
|
| select(.key != "env_vars")
|
||||||
|
| select((.value|type) != "array" and (.value|type) != "object" and (.value|type) != "null")
|
||||||
|
| emit(.key; .value)
|
||||||
|
' "$JSONSOURCE"
|
||||||
|
)
|
||||||
|
|
||||||
if [[ "$jtype" == "array" ]]; then
|
# -----------------------------------------------------------------------------
|
||||||
if [[ "$KEYS" == "env_vars" ]]; then
|
# Write /.env and /etc/environment in an idempotent way (no infinite appends)
|
||||||
mapfile -t env_entries < <(jq -c '.env_vars[]?' "$JSONSOURCE")
|
# -----------------------------------------------------------------------------
|
||||||
for entry in "${env_entries[@]}"; do
|
# Ensure /.env has a header (only if missing)
|
||||||
if [[ "$entry" == \{* ]]; then
|
if [[ ! -f "$ENV_FILE" ]]; then
|
||||||
env_name="$(jq -r 'if has("name") and has("value") then .name else empty end' <<<"$entry")"
|
printf '# Generated by 00-global_var.sh from %s\n' "$JSONSOURCE" > "$ENV_FILE"
|
||||||
if [[ -n "$env_name" ]]; then
|
fi
|
||||||
env_value="$(jq -r '.value // "" | tostring' <<<"$entry")"
|
|
||||||
export_option "$env_name" "$env_value"
|
|
||||||
else
|
|
||||||
mapfile -t env_keys < <(jq -r 'keys[]' <<<"$entry")
|
|
||||||
for env_key in "${env_keys[@]}"; do
|
|
||||||
env_value="$(jq -r --arg k "$env_key" '.[$k] // "" | tostring' <<<"$entry")"
|
|
||||||
export_option "$env_key" "$env_value"
|
|
||||||
done
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
env_pair="$(jq -r '.' <<<"$entry")"
|
|
||||||
if [[ "$env_pair" == *=* ]]; then
|
|
||||||
export_option "${env_pair%%=*}" "${env_pair#*=}"
|
|
||||||
else
|
|
||||||
bashio::log.warning "env_vars entry '$env_pair' is not in KEY=VALUE format, skipping"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
else
|
|
||||||
bashio::log.warning "Option '${KEYS}' is an array, skipping"
|
|
||||||
fi
|
|
||||||
elif [[ "$jtype" == "object" ]]; then
|
|
||||||
bashio::log.warning "Option '${KEYS}' is an object, skipping"
|
|
||||||
elif [[ "$jtype" == "null" ]]; then
|
|
||||||
continue
|
|
||||||
else
|
|
||||||
VALUE="$(jq -r --arg k "$KEYS" '.[$k] // "" | tostring' "$JSONSOURCE")"
|
|
||||||
export_option "$KEYS" "$VALUE"
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
|
ENV_FILE_BLOCK="$(mktemp_safe)"
|
||||||
|
trap 'rm -f "$EXPORT_BLOCK_FILE" "$ENV_KV_FILE" "$ENV_FILE_BLOCK"' EXIT
|
||||||
|
|
||||||
|
{
|
||||||
|
echo "${FILE_BLOCK_BEGIN}"
|
||||||
|
echo "# Do not edit: generated from ${JSONSOURCE}"
|
||||||
|
cat "$ENV_KV_FILE" 2>/dev/null || true
|
||||||
|
echo "${FILE_BLOCK_END}"
|
||||||
|
} > "$ENV_FILE_BLOCK"
|
||||||
|
|
||||||
|
replace_block_in_file "$ENV_FILE" "$FILE_BLOCK_BEGIN" "$FILE_BLOCK_END" "$ENV_FILE_BLOCK"
|
||||||
|
replace_block_in_file "$ETC_ENV_FILE" "$FILE_BLOCK_BEGIN" "$FILE_BLOCK_END" "$ENV_FILE_BLOCK"
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# Inject generated export block into service scripts and interactive shells
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
update_scripts_with_block
|
update_scripts_with_block
|
||||||
|
|
||||||
# --- MINIMAL CHANGE: also inject into /etc/bash.bashrc (for interactive bash shells)
|
# System-wide interactive bash shells
|
||||||
mkdir -p /etc
|
|
||||||
touch "/etc/bash.bashrc"
|
touch "/etc/bash.bashrc"
|
||||||
inject_block_into_file "/etc/bash.bashrc"
|
inject_block_into_file "/etc/bash.bashrc"
|
||||||
|
|
||||||
|
# Common per-user interactive bash shell config (more standard than bash.bashrc)
|
||||||
if [[ -n "${HOME:-}" ]]; then
|
if [[ -n "${HOME:-}" ]]; then
|
||||||
mkdir -p "$HOME"
|
mkdir -p "$HOME"
|
||||||
|
touch "$HOME/.bashrc"
|
||||||
|
inject_block_into_file "$HOME/.bashrc"
|
||||||
|
|
||||||
|
# Kept for compatibility with images that use this non-standard name
|
||||||
touch "$HOME/bash.bashrc"
|
touch "$HOME/bash.bashrc"
|
||||||
inject_block_into_file "$HOME/bash.bashrc"
|
inject_block_into_file "$HOME/bash.bashrc"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
################
|
# -----------------------------------------------------------------------------
|
||||||
# Set timezone #
|
# Set timezone (kept identical in behavior)
|
||||||
################
|
# -----------------------------------------------------------------------------
|
||||||
set +eu
|
set +eu
|
||||||
|
|
||||||
if [ -n "$TZ" ] && [ -f /etc/localtime ]; then
|
if [ -n "$TZ" ] && [ -f /etc/localtime ]; then
|
||||||
|
|||||||
Reference in New Issue
Block a user