4108121984
Replace the hardcoded MUSICBRAINZ_USER_AGENT placeholder in env.template with an optional MUSICBRAINZ_OWNER_EMAIL prompt in step_enrichment. The backend now composes a valid User-Agent from app name + version + this email (falling back to the project URL if left blank).
136 lines
5.4 KiB
Bash
136 lines
5.4 KiB
Bash
# Pure-bash prompt helpers — no external TUI, no dependencies.
|
|
#
|
|
# Every prompt shows its default, re-asks on invalid input, and lets Ctrl+C
|
|
# abort cleanly (the trap is installed in deploy.sh). Results are returned via
|
|
# the REPLY-style globals documented on each function.
|
|
|
|
# -- colours (disabled if not a TTY or NO_COLOR set) -----------------------
|
|
if [[ -t 1 && -z "${NO_COLOR:-}" ]]; then
|
|
C_RESET=$'\033[0m'; C_BOLD=$'\033[1m'; C_DIM=$'\033[2m'
|
|
C_CYAN=$'\033[36m'; C_GREEN=$'\033[32m'; C_YELLOW=$'\033[33m'; C_RED=$'\033[31m'
|
|
else
|
|
C_RESET=''; C_BOLD=''; C_DIM=''; C_CYAN=''; C_GREEN=''; C_YELLOW=''; C_RED=''
|
|
fi
|
|
|
|
ui_title() { printf '\n%s%s%s\n' "$C_BOLD$C_CYAN" "$1" "$C_RESET"; }
|
|
ui_info() { printf '%s\n' "$1"; }
|
|
ui_dim() { printf '%s%s%s\n' "$C_DIM" "$1" "$C_RESET"; }
|
|
ui_ok() { printf '%s✓ %s%s\n' "$C_GREEN" "$1" "$C_RESET"; }
|
|
ui_warn() { printf '%s! %s%s\n' "$C_YELLOW" "$1" "$C_RESET"; }
|
|
ui_err() { printf '%s✗ %s%s\n' "$C_RED" "$1" "$C_RESET" >&2; }
|
|
|
|
# ui_input PROMPT DEFAULT [VALIDATOR_FN]
|
|
# Sets UI_VALUE. VALIDATOR_FN (optional) receives the candidate value and
|
|
# returns 0 to accept; on rejection it should print why (to stderr).
|
|
ui_input() {
|
|
local prompt="$1" default="${2:-}" validator="${3:-}" ans
|
|
while true; do
|
|
if [[ -n "$default" ]]; then
|
|
read -r -p "$prompt [${C_DIM}${default}${C_RESET}]: " ans || exit 130
|
|
ans="${ans:-$default}"
|
|
else
|
|
read -r -p "$prompt: " ans || exit 130
|
|
fi
|
|
if [[ -n "$validator" ]]; then
|
|
if ! "$validator" "$ans"; then continue; fi
|
|
fi
|
|
UI_VALUE="$ans"
|
|
return 0
|
|
done
|
|
}
|
|
|
|
# ui_secret PROMPT — no echo. Sets UI_VALUE.
|
|
ui_secret() {
|
|
local prompt="$1" ans
|
|
read -r -s -p "$prompt: " ans || exit 130
|
|
echo
|
|
UI_VALUE="$ans"
|
|
}
|
|
|
|
# ui_yesno PROMPT DEFAULT(yes|no) — sets UI_BOOL to "yes"/"no", returns 0/1.
|
|
ui_yesno() {
|
|
local prompt="$1" default="${2:-yes}" ans hint
|
|
[[ "$default" == "yes" ]] && hint="[Y/n]" || hint="[y/N]"
|
|
while true; do
|
|
read -r -p "$prompt $hint: " ans || exit 130
|
|
ans="${ans:-$default}"
|
|
case "${ans,,}" in
|
|
y|yes|да|д) UI_BOOL="yes"; return 0 ;;
|
|
n|no|нет|н) UI_BOOL="no"; return 1 ;;
|
|
*) ui_warn "$(t invalid_input)" ;;
|
|
esac
|
|
done
|
|
}
|
|
|
|
# ui_select PROMPT "label1" "label2" ...
|
|
# Numbered single-choice menu. Sets UI_INDEX (1-based) and UI_VALUE (label).
|
|
ui_select() {
|
|
local prompt="$1"; shift
|
|
local -a opts=("$@")
|
|
local i ans
|
|
ui_info "$prompt"
|
|
for i in "${!opts[@]}"; do
|
|
printf ' %s%d%s) %s\n' "$C_CYAN" "$((i + 1))" "$C_RESET" "${opts[$i]}"
|
|
done
|
|
while true; do
|
|
read -r -p "> [${C_DIM}1${C_RESET}]: " ans || exit 130
|
|
ans="${ans:-1}"
|
|
if [[ "$ans" =~ ^[0-9]+$ ]] && ((ans >= 1 && ans <= ${#opts[@]})); then
|
|
UI_INDEX="$ans"
|
|
UI_VALUE="${opts[$((ans - 1))]}"
|
|
return 0
|
|
fi
|
|
ui_warn "$(t invalid_input)"
|
|
done
|
|
}
|
|
|
|
# ui_multiselect PROMPT "key1:label1:on" "key2:label2:off" "key3:label3:locked"
|
|
# Toggle items by number; 'locked' items cannot be turned off. Empty line
|
|
# confirms. Sets UI_SELECTED (space-separated keys that ended up on).
|
|
ui_multiselect() {
|
|
local prompt="$1"; shift
|
|
local -a keys=() labels=() states=() locks=()
|
|
local spec key label state
|
|
for spec in "$@"; do
|
|
key="${spec%%:*}"; spec="${spec#*:}"
|
|
label="${spec%%:*}"; state="${spec#*:}"
|
|
keys+=("$key"); labels+=("$label")
|
|
if [[ "$state" == "locked" ]]; then states+=("on"); locks+=("yes")
|
|
else states+=("$state"); locks+=("no"); fi
|
|
done
|
|
local i ans
|
|
while true; do
|
|
ui_info "$prompt"
|
|
for i in "${!keys[@]}"; do
|
|
local mark="[ ]"; [[ "${states[$i]}" == "on" ]] && mark="[x]"
|
|
local lock=""; [[ "${locks[$i]}" == "yes" ]] && lock=" ${C_DIM}(required)${C_RESET}"
|
|
printf ' %s%d%s) %s %s%s\n' "$C_CYAN" "$((i + 1))" "$C_RESET" "$mark" "${labels[$i]}" "$lock"
|
|
done
|
|
ui_dim "$(t enter_to_keep) — number toggles, Enter confirms"
|
|
read -r -p "> " ans || exit 130
|
|
[[ -z "$ans" ]] && break
|
|
if [[ "$ans" =~ ^[0-9]+$ ]] && ((ans >= 1 && ans <= ${#keys[@]})); then
|
|
local idx=$((ans - 1))
|
|
if [[ "${locks[$idx]}" == "yes" ]]; then
|
|
ui_warn "$(t backend_required)"
|
|
elif [[ "${states[$idx]}" == "on" ]]; then states[$idx]="off"
|
|
else states[$idx]="on"; fi
|
|
else
|
|
ui_warn "$(t invalid_input)"
|
|
fi
|
|
done
|
|
UI_SELECTED=""
|
|
for i in "${!keys[@]}"; do
|
|
[[ "${states[$i]}" == "on" ]] && UI_SELECTED+="${keys[$i]} "
|
|
done
|
|
UI_SELECTED="${UI_SELECTED% }"
|
|
}
|
|
|
|
# -- common validators (for ui_input) --------------------------------------
|
|
v_nonempty() { [[ -n "$1" ]] || { ui_warn "$(t invalid_input)"; return 1; }; }
|
|
v_port() { [[ "$1" =~ ^[0-9]+$ ]] && (($1 >= 1 && $1 <= 65535)) || { ui_warn "$(t invalid_input)"; return 1; }; }
|
|
v_url() { [[ "$1" =~ ^https?:// ]] || { ui_warn "$(t invalid_input)"; return 1; }; }
|
|
v_redis() { [[ "$1" =~ ^rediss?:// ]] || { ui_warn "$(t invalid_input)"; return 1; }; }
|
|
v_domain() { [[ "$1" =~ ^[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$ ]] || { ui_warn "$(t invalid_input)"; return 1; }; }
|
|
v_email_opt() { [[ -z "$1" || "$1" =~ ^[^[:space:]@]+@[^[:space:]@]+\.[a-zA-Z]{2,}$ ]] || { ui_warn "$(t invalid_input)"; return 1; }; }
|