Add prompt_reinstall_mode helper and document the update/fresh/cancel convention
Replaces the y/n "update in place?" prompt in asterisk.sh/asterisk-do.sh with an explicit r/f/c choice — (r)einstall in place, (f)ull install, (c)ancel — defaulting to cancel on a bare Enter (or Ctrl-D) instead of falling through to a destructive full reinstall. Adds prompt_reinstall_mode to lib/common.sh (plus matching standalone stubs in both asterisk scripts for when they run without the full repo) and documents the convention in CLAUDE.md: any service with a persistent install directory should offer this choice on rerun instead of re-asking every prompt just to pick up a script fix.
This commit is contained in:
@@ -125,9 +125,12 @@ log_error "message" # red [ERROR]
|
|||||||
```bash
|
```bash
|
||||||
prompt_yn "Question? (y/n):" "default_y_or_n" VARNAME
|
prompt_yn "Question? (y/n):" "default_y_or_n" VARNAME
|
||||||
prompt_text "Question? [default]:" "default" VARNAME
|
prompt_text "Question? [default]:" "default" VARNAME
|
||||||
|
prompt_reinstall_mode VARNAME # sets VARNAME to: update | fresh | cancel
|
||||||
```
|
```
|
||||||
|
|
||||||
When `UNATTENDED=true` both functions skip the prompt and use the default.
|
When `UNATTENDED=true` all three skip the prompt; `prompt_yn`/`prompt_text` use
|
||||||
|
their given default, `prompt_reinstall_mode` always resolves to `cancel`. See
|
||||||
|
**Update vs. fresh reinstall on rerun** below for how to use the latter.
|
||||||
|
|
||||||
### Pre-flight
|
### Pre-flight
|
||||||
|
|
||||||
@@ -262,6 +265,51 @@ fi
|
|||||||
Put the check early — after any pure-display output (banners, info text)
|
Put the check early — after any pure-display output (banners, info text)
|
||||||
but before the first write.
|
but before the first write.
|
||||||
|
|
||||||
|
## Update vs. fresh reinstall on rerun
|
||||||
|
|
||||||
|
Every service should detect an existing install at the top of its
|
||||||
|
`install_<name>()` — after the `DRY_RUN` check, before any prompts — and
|
||||||
|
offer `prompt_reinstall_mode` instead of silently re-running every prompt
|
||||||
|
(domain, secrets, firewall, Authelia, extras...) from scratch. What counts
|
||||||
|
as "already installed" is service-specific: usually `docker-compose.yml` and
|
||||||
|
`.env` both existing in the service's `$DOCKER_DIR/<name>` directory.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
if [[ -f "$DIR/docker-compose.yml" && -f "$DIR/.env" ]]; then
|
||||||
|
local MODE=""
|
||||||
|
prompt_reinstall_mode MODE
|
||||||
|
case "$MODE" in
|
||||||
|
update)
|
||||||
|
# Refresh vendor files / config templates, rebuild, done.
|
||||||
|
# Do NOT touch .env, firewall rules, or Caddy/Authelia config.
|
||||||
|
...
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
cancel)
|
||||||
|
log_info "Leaving the existing install as-is."
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
fresh) ;; # fall through to the full install flow below
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
```
|
||||||
|
|
||||||
|
`update` should be genuinely non-destructive: refresh whatever the service
|
||||||
|
vendors or templates (Docker image sources, config templates,
|
||||||
|
`docker-compose.yml`) and rebuild/restart, but never touch `.env`, firewall
|
||||||
|
rules, or reverse-proxy/SSO config that's already in place. If the
|
||||||
|
vendor-copy or `docker-compose.yml`-generation logic is more than a few
|
||||||
|
lines, factor it into a helper function so the fresh-install path and the
|
||||||
|
update path share one copy instead of drifting apart — see
|
||||||
|
`_asterisk_do_refresh_vendor_files`/`_asterisk_do_write_compose` in
|
||||||
|
`services/asterisk-do.sh` (and their `_asterisk_*` counterparts in
|
||||||
|
`services/asterisk.sh`) for the reference pattern.
|
||||||
|
|
||||||
|
`cancel` must leave the install completely untouched — it's the default for
|
||||||
|
a reason (a stray Enter on a service you're just checking on shouldn't
|
||||||
|
trigger anything). `fresh` runs the exact same flow a first-time install
|
||||||
|
would, prompts included.
|
||||||
|
|
||||||
## .env files and secrets
|
## .env files and secrets
|
||||||
|
|
||||||
Generate passwords with `generate_password` (never hardcode them).
|
Generate passwords with `generate_password` (never hardcode them).
|
||||||
|
|||||||
@@ -362,6 +362,32 @@ prompt_text() {
|
|||||||
eval "$varname='${response:-$default}'"
|
eval "$varname='${response:-$default}'"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Prompt for how to handle a service that's already installed, honoring
|
||||||
|
# unattended. prompt_reinstall_mode VARNAME
|
||||||
|
# Sets VARNAME to one of: update | fresh | cancel
|
||||||
|
# Enter (no input) and any unrecognized input both resolve to "cancel" — this
|
||||||
|
# guards a destructive full reinstall behind a deliberate keypress instead of
|
||||||
|
# a stray Enter. Unattended mode always resolves to "cancel" too: never
|
||||||
|
# silently touch an existing install when nobody's watching the prompt.
|
||||||
|
prompt_reinstall_mode() {
|
||||||
|
local varname="$1" response
|
||||||
|
if [ "$UNATTENDED" = true ]; then
|
||||||
|
eval "$varname='cancel'"
|
||||||
|
echo "Existing install detected — leaving it as-is [auto: cancel, unattended mode]"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
echo " Existing install detected. Choose:"
|
||||||
|
echo " r) Reinstall in place — refresh vendor files/config, keep existing settings"
|
||||||
|
echo " f) Full install — re-run every prompt from scratch"
|
||||||
|
echo " c) Cancel — leave everything as-is [default]"
|
||||||
|
read -p " Choice [r/f/c, Enter=cancel]: " response
|
||||||
|
case "${response,,}" in
|
||||||
|
r) eval "$varname='update'" ;;
|
||||||
|
f) eval "$varname='fresh'" ;;
|
||||||
|
*) eval "$varname='cancel'" ;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
# ── Per-service README generation ────────────────────────────────────────────
|
# ── Per-service README generation ────────────────────────────────────────────
|
||||||
# Write <dir>/README.md from stdin (markdown). Every module is encouraged to
|
# Write <dir>/README.md from stdin (markdown). Every module is encouraged to
|
||||||
# call this so each ~/docker/<service>/ folder is self-documenting.
|
# call this so each ~/docker/<service>/ folder is self-documenting.
|
||||||
|
|||||||
+57
-30
@@ -61,6 +61,25 @@ if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
|
|||||||
eval "$_var='${_r:-$_def}'"
|
eval "$_var='${_r:-$_def}'"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
prompt_reinstall_mode() {
|
||||||
|
local _var="$1" _r
|
||||||
|
if [[ "${UNATTENDED:-false}" == "true" ]]; then
|
||||||
|
eval "$_var='cancel'"
|
||||||
|
echo "Existing install detected — leaving it as-is [auto: cancel, unattended mode]"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
echo " Existing install detected. Choose:"
|
||||||
|
echo " r) Reinstall in place — refresh vendor files/config, keep existing settings"
|
||||||
|
echo " f) Full install — re-run every prompt from scratch"
|
||||||
|
echo " c) Cancel — leave everything as-is [default]"
|
||||||
|
read -r -p " Choice [r/f/c, Enter=cancel]: " _r
|
||||||
|
case "${_r,,}" in
|
||||||
|
r) eval "$_var='update'" ;;
|
||||||
|
f) eval "$_var='fresh'" ;;
|
||||||
|
*) eval "$_var='cancel'" ;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
configure_caddy_for_service() {
|
configure_caddy_for_service() {
|
||||||
local _name="$1" _upstream="$2" _subdomain="$3" _extra="${4:-}"
|
local _name="$1" _upstream="$2" _subdomain="$3" _extra="${4:-}"
|
||||||
local _caddy_dir="$DOCKER_DIR/caddy"
|
local _caddy_dir="$DOCKER_DIR/caddy"
|
||||||
@@ -353,39 +372,47 @@ install_asterisk-do() {
|
|||||||
if [[ -f "$EA_DIR/docker-compose.yml" && -f "$EA_DIR/.env" ]]; then
|
if [[ -f "$EA_DIR/docker-compose.yml" && -f "$EA_DIR/.env" ]]; then
|
||||||
echo ""
|
echo ""
|
||||||
log_info "Existing install found at $EA_DIR."
|
log_info "Existing install found at $EA_DIR."
|
||||||
local UPDATE_INPLACE=""
|
local REINSTALL_MODE=""
|
||||||
prompt_yn "Update in place (refresh vendor files + docker-compose.yml, keep your existing domain/firewall/Authelia settings) instead of a full fresh reinstall? (y/n):" "y" UPDATE_INPLACE
|
prompt_reinstall_mode REINSTALL_MODE
|
||||||
if [[ "$UPDATE_INPLACE" =~ ^[Yy]$ ]]; then
|
case "$REINSTALL_MODE" in
|
||||||
mkdir -p "$EA_DIR/config/asterisk" "$EA_DIR/config/easy-asterisk" \
|
update)
|
||||||
"$EA_DIR/logs" "$EA_DIR/spool" "$EA_DIR/lib" "$EA_DIR/exports"
|
mkdir -p "$EA_DIR/config/asterisk" "$EA_DIR/config/easy-asterisk" \
|
||||||
ensure_docker_dir_ownership "$EA_DIR"
|
"$EA_DIR/logs" "$EA_DIR/spool" "$EA_DIR/lib" "$EA_DIR/exports"
|
||||||
cd "$EA_DIR" || return 1
|
ensure_docker_dir_ownership "$EA_DIR"
|
||||||
|
cd "$EA_DIR" || return 1
|
||||||
|
|
||||||
_asterisk_do_refresh_vendor_files
|
_asterisk_do_refresh_vendor_files
|
||||||
_asterisk_do_write_compose
|
_asterisk_do_write_compose
|
||||||
|
|
||||||
log_info "Rebuilding and restarting containers..."
|
log_info "Rebuilding and restarting containers..."
|
||||||
if docker compose up -d --build --force-recreate; then
|
if docker compose up -d --build --force-recreate; then
|
||||||
log_success "Update complete — vendor files and docker-compose.yml refreshed."
|
log_success "Update complete — vendor files and docker-compose.yml refreshed."
|
||||||
else
|
else
|
||||||
log_warning "docker compose up failed — check: docker compose -f $EA_DIR/docker-compose.yml logs"
|
log_warning "docker compose up failed — check: docker compose -f $EA_DIR/docker-compose.yml logs"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
local _EXISTING_DOMAIN _EXISTING_PORT
|
local _EXISTING_DOMAIN _EXISTING_PORT
|
||||||
_EXISTING_DOMAIN="$(grep -E '^DOMAIN_NAME=' .env | cut -d= -f2-)"
|
_EXISTING_DOMAIN="$(grep -E '^DOMAIN_NAME=' .env | cut -d= -f2-)"
|
||||||
_EXISTING_PORT="$(grep -E '^WEB_ADMIN_PORT=' .env | cut -d= -f2-)"
|
_EXISTING_PORT="$(grep -E '^WEB_ADMIN_PORT=' .env | cut -d= -f2-)"
|
||||||
echo ""
|
echo ""
|
||||||
log_success "Existing .env, UFW rules, Cloud Firewall, and Caddy/Authelia config were left untouched."
|
log_success "Existing .env, UFW rules, Cloud Firewall, and Caddy/Authelia config were left untouched."
|
||||||
if [[ -n "$_EXISTING_DOMAIN" ]]; then
|
if [[ -n "$_EXISTING_DOMAIN" ]]; then
|
||||||
echo " Web admin: https://${_EXISTING_DOMAIN}/"
|
echo " Web admin: https://${_EXISTING_DOMAIN}/"
|
||||||
else
|
else
|
||||||
echo " Web admin: http://<droplet-ip>:${_EXISTING_PORT:-8081}"
|
echo " Web admin: http://<droplet-ip>:${_EXISTING_PORT:-8081}"
|
||||||
fi
|
fi
|
||||||
echo " Logs: docker compose -f $EA_DIR/docker-compose.yml logs -f"
|
echo " Logs: docker compose -f $EA_DIR/docker-compose.yml logs -f"
|
||||||
echo ""
|
echo ""
|
||||||
return 0
|
return 0
|
||||||
fi
|
;;
|
||||||
log_info "Proceeding with a full fresh reinstall — existing config will be reused where prompts match, everything else re-asked."
|
cancel)
|
||||||
|
log_info "Leaving the existing install as-is — nothing changed."
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
fresh)
|
||||||
|
log_info "Proceeding with a full fresh reinstall — every prompt below runs from scratch."
|
||||||
|
;;
|
||||||
|
esac
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# ── Bring in base first, if this is a genuinely fresh box ─────────────────
|
# ── Bring in base first, if this is a genuinely fresh box ─────────────────
|
||||||
|
|||||||
+57
-30
@@ -59,6 +59,25 @@ if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
|
|||||||
eval "$_var='${_r:-$_def}'"
|
eval "$_var='${_r:-$_def}'"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
prompt_reinstall_mode() {
|
||||||
|
local _var="$1" _r
|
||||||
|
if [[ "${UNATTENDED:-false}" == "true" ]]; then
|
||||||
|
eval "$_var='cancel'"
|
||||||
|
echo "Existing install detected — leaving it as-is [auto: cancel, unattended mode]"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
echo " Existing install detected. Choose:"
|
||||||
|
echo " r) Reinstall in place — refresh vendor files/config, keep existing settings"
|
||||||
|
echo " f) Full install — re-run every prompt from scratch"
|
||||||
|
echo " c) Cancel — leave everything as-is [default]"
|
||||||
|
read -r -p " Choice [r/f/c, Enter=cancel]: " _r
|
||||||
|
case "${_r,,}" in
|
||||||
|
r) eval "$_var='update'" ;;
|
||||||
|
f) eval "$_var='fresh'" ;;
|
||||||
|
*) eval "$_var='cancel'" ;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
configure_caddy_for_service() {
|
configure_caddy_for_service() {
|
||||||
local _name="$1" _upstream="$2" _subdomain="$3" _extra="${4:-}"
|
local _name="$1" _upstream="$2" _subdomain="$3" _extra="${4:-}"
|
||||||
local _caddy_dir="$DOCKER_DIR/caddy"
|
local _caddy_dir="$DOCKER_DIR/caddy"
|
||||||
@@ -333,39 +352,47 @@ install_asterisk() {
|
|||||||
if [[ -f "$EA_DIR/docker-compose.yml" && -f "$EA_DIR/.env" ]]; then
|
if [[ -f "$EA_DIR/docker-compose.yml" && -f "$EA_DIR/.env" ]]; then
|
||||||
echo ""
|
echo ""
|
||||||
log_info "Existing install found at $EA_DIR."
|
log_info "Existing install found at $EA_DIR."
|
||||||
local UPDATE_INPLACE=""
|
local REINSTALL_MODE=""
|
||||||
prompt_yn "Update in place (refresh vendor files + docker-compose.yml, keep your existing domain/VLAN/Authelia settings) instead of a full fresh reinstall? (y/n):" "y" UPDATE_INPLACE
|
prompt_reinstall_mode REINSTALL_MODE
|
||||||
if [[ "$UPDATE_INPLACE" =~ ^[Yy]$ ]]; then
|
case "$REINSTALL_MODE" in
|
||||||
mkdir -p "$EA_DIR/config/asterisk" "$EA_DIR/config/easy-asterisk" \
|
update)
|
||||||
"$EA_DIR/logs" "$EA_DIR/spool" "$EA_DIR/lib" "$EA_DIR/exports"
|
mkdir -p "$EA_DIR/config/asterisk" "$EA_DIR/config/easy-asterisk" \
|
||||||
ensure_docker_dir_ownership "$EA_DIR"
|
"$EA_DIR/logs" "$EA_DIR/spool" "$EA_DIR/lib" "$EA_DIR/exports"
|
||||||
cd "$EA_DIR" || return 1
|
ensure_docker_dir_ownership "$EA_DIR"
|
||||||
|
cd "$EA_DIR" || return 1
|
||||||
|
|
||||||
_asterisk_refresh_vendor_files
|
_asterisk_refresh_vendor_files
|
||||||
_asterisk_write_compose
|
_asterisk_write_compose
|
||||||
|
|
||||||
log_info "Rebuilding and restarting containers..."
|
log_info "Rebuilding and restarting containers..."
|
||||||
if docker compose up -d --build --force-recreate; then
|
if docker compose up -d --build --force-recreate; then
|
||||||
log_success "Update complete — vendor files and docker-compose.yml refreshed."
|
log_success "Update complete — vendor files and docker-compose.yml refreshed."
|
||||||
else
|
else
|
||||||
log_warning "docker compose up failed — check: docker compose -f $EA_DIR/docker-compose.yml logs"
|
log_warning "docker compose up failed — check: docker compose -f $EA_DIR/docker-compose.yml logs"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
local _EXISTING_DOMAIN _EXISTING_PORT
|
local _EXISTING_DOMAIN _EXISTING_PORT
|
||||||
_EXISTING_DOMAIN="$(grep -E '^DOMAIN_NAME=' .env | cut -d= -f2-)"
|
_EXISTING_DOMAIN="$(grep -E '^DOMAIN_NAME=' .env | cut -d= -f2-)"
|
||||||
_EXISTING_PORT="$(grep -E '^WEB_ADMIN_PORT=' .env | cut -d= -f2-)"
|
_EXISTING_PORT="$(grep -E '^WEB_ADMIN_PORT=' .env | cut -d= -f2-)"
|
||||||
echo ""
|
echo ""
|
||||||
log_success "Existing .env, UFW rules, and Caddy/Authelia config were left untouched."
|
log_success "Existing .env, UFW rules, and Caddy/Authelia config were left untouched."
|
||||||
if [[ -n "$_EXISTING_DOMAIN" ]]; then
|
if [[ -n "$_EXISTING_DOMAIN" ]]; then
|
||||||
echo " Web admin: https://${_EXISTING_DOMAIN}/"
|
echo " Web admin: https://${_EXISTING_DOMAIN}/"
|
||||||
else
|
else
|
||||||
echo " Web admin: http://$(hostname -I 2>/dev/null | awk '{print $1}' || echo localhost):${_EXISTING_PORT:-8081}"
|
echo " Web admin: http://$(hostname -I 2>/dev/null | awk '{print $1}' || echo localhost):${_EXISTING_PORT:-8081}"
|
||||||
fi
|
fi
|
||||||
echo " Logs: docker compose -f $EA_DIR/docker-compose.yml logs -f"
|
echo " Logs: docker compose -f $EA_DIR/docker-compose.yml logs -f"
|
||||||
echo ""
|
echo ""
|
||||||
return 0
|
return 0
|
||||||
fi
|
;;
|
||||||
log_info "Proceeding with a full fresh reinstall — existing config will be reused where prompts match, everything else re-asked."
|
cancel)
|
||||||
|
log_info "Leaving the existing install as-is — nothing changed."
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
fresh)
|
||||||
|
log_info "Proceeding with a full fresh reinstall — every prompt below runs from scratch."
|
||||||
|
;;
|
||||||
|
esac
|
||||||
fi
|
fi
|
||||||
|
|
||||||
mkdir -p "$EA_DIR"
|
mkdir -p "$EA_DIR"
|
||||||
|
|||||||
Reference in New Issue
Block a user