diff --git a/Partnership/Manual-Partnership.md b/Partnership/Manual-Partnership.md index 30102c8..9d58610 100644 --- a/Partnership/Manual-Partnership.md +++ b/Partnership/Manual-Partnership.md @@ -371,6 +371,29 @@ Counter resets after `SSH_STRIKE_RESET_HRS` of clean connectivity. --- +## ━━━ OUTPUT TIERS ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +All scripts use a two-tier output model: `echo` lines are always visible; `log` +lines only appear when `--log` is passed. + +**partnership_onboard.sh** — one-shot setup. Without `--log`, step headers, per-step +result lines (deployed/failed counts), and the final summary are visible. Per-container +deploy detail suppressed. + +**partnership_offboard.sh** — one-shot teardown. Without `--log`, step headers, key +state transitions, and the final checklist summary are visible. Per-container cleanup +detail suppressed. + +**partnership_manager.sh** — check/status/transfer/onboard modes. Without `--log`, +mode-specific result lines (`--check` prints ACTIVE/INACTIVE status always), state +transitions, warnings, and summary blocks are visible. Per-operation detail suppressed. + +**ssh_setup.sh** — one-shot key setup. Without `--log`, section headers and per-step +results (key created, key installed, auth verified) are visible. Strike-counter +management (`--validate`) uses `log` for healthy cycles; `warn` for failures. + +--- + ## ━━━ FLAG REFERENCE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ### partnership_onboard.sh diff --git a/Partnership/partnership_manager.sh b/Partnership/partnership_manager.sh index 973ad3f..9522e57 100644 --- a/Partnership/partnership_manager.sh +++ b/Partnership/partnership_manager.sh @@ -270,6 +270,21 @@ OWNER_STATE_FILE="/boot/config/partnership_${OWNER}.db" MIRROR_STATE_FILE="/boot/config/partnership_${MIRROR}.db" OFFLINE_COUNTER="/boot/config/partnership_offline_days.db" +# ── Exit Trap — restart locally stopped containers if script crashes mid-cleanup ────────────── +# Used by folderview3_remove_partner_folder() and cleanup_partner_containers() — also shared +# with partnership_offboard.sh which sources this file and registers the same trap. +declare -a _PM_TRAP_STOPPED=() +_pm_trap_restart_stopped() { + [[ ${#_PM_TRAP_STOPPED[@]} -eq 0 ]] && return + for c in "${_PM_TRAP_STOPPED[@]}"; do + [[ -z "$c" ]] && continue + if docker inspect "$c" >/dev/null 2>&1; then + warn "Exit trap: restarting $c (stopped but not removed)" + docker start "$c" >/dev/null 2>&1 || warn " Failed to restart $c" + fi + done +} + if [[ "${PARTNERSHIP_LIB_MODE:-}" != "1" ]]; then if [[ -z "$MODE" ]]; then error "No mode specified" @@ -297,6 +312,8 @@ if [[ "${PARTNERSHIP_LIB_MODE:-}" != "1" ]]; then # Lock for all modes except --check (frequent) and --offboard (offboard script holds its own) [[ "$MODE" != "check" && "$MODE" != "offboard" ]] && acquire_lock "strict" + trap _pm_trap_restart_stopped EXIT + [[ "$DRY_RUN" == true ]] && warn "DRY RUN — no changes will be made" fi @@ -679,6 +696,7 @@ folderview3_remove_partner_folder() { if timeout "${DOCKER_TIMEOUT:-30}" docker inspect "$container" >/dev/null 2>&1; then if timeout "${DOCKER_TIMEOUT:-30}" docker stop "$container" >/dev/null 2>&1; then log "$container stopped ✅" + _PM_TRAP_STOPPED+=("$container") (( stopped++ )) else warn "$container stop failed" @@ -822,6 +840,7 @@ cleanup_partner_containers() { fi if timeout "${DOCKER_TIMEOUT:-30}" docker inspect "$container" >/dev/null 2>&1; then timeout "${DOCKER_TIMEOUT:-30}" docker stop "$container" >/dev/null 2>&1 || true + _PM_TRAP_STOPPED+=("$container") timeout "${DOCKER_TIMEOUT:-30}" docker rm "$container" >/dev/null 2>&1 && \ log "$container removed ✅" || warn "$container rm failed" else @@ -1337,7 +1356,7 @@ if [[ "$MODE" == "check" ]]; then # Both agree and active — healthy, silent if [[ "$LOCAL_STATE" == "$REMOTE_STATE" ]] && [[ "$LOCAL_STATE" == "ACTIVE" ]]; then - log "Partnership check — ACTIVE, both servers agree ✅" + echo "Partnership check — ACTIVE, both servers agree ✅" exit 0 fi @@ -1397,7 +1416,7 @@ if [[ "$MODE" == "check" ]]; then # Both inactive — nothing to do if [[ "$LOCAL_STATE" == "INACTIVE" ]] && [[ "$REMOTE_STATE" == "INACTIVE" ]]; then - log "Partnership check — INACTIVE on both servers" + echo "Partnership check — INACTIVE on both servers" exit 0 fi @@ -1489,7 +1508,7 @@ if [[ "$MODE" == "onboard" ]]; then if [[ "$DRY_RUN" == false ]]; then write_state_file "$LOCAL_STATE_FILE" "ACTIVE" "$NOW" "" "$LOCAL_SERVER_NAME" "onboard" - log "Local state: ACTIVE ✅" + echo "Local state: ACTIVE ✅" remove_from_blocklist "$MIRROR" push_state_to_remote "$LOCAL_STATE_FILE" "$MIRROR_IP" "$MIRROR_SSH_KEY" echo "0" > "$OFFLINE_COUNTER" diff --git a/Partnership/partnership_offboard.sh b/Partnership/partnership_offboard.sh index d1dd46b..09bc1fc 100755 --- a/Partnership/partnership_offboard.sh +++ b/Partnership/partnership_offboard.sh @@ -124,6 +124,8 @@ OFFLINE_COUNTER="/boot/config/partnership_offline_days.db" acquire_lock "strict" +trap _pm_trap_restart_stopped EXIT + # Check already offboarded if [[ -f "$LOCAL_STATE_FILE" ]]; then CURRENT_STATE=$(read_state_file "$LOCAL_STATE_FILE" "state") @@ -265,6 +267,7 @@ cleanup_deployed_stack_locally() { --format '{{range .HostConfig.Binds}}{{println .}}{{end}}' \ "$cname" 2>/dev/null | awk -F: '{print $1}' | grep '^/mnt/.*/appdata') timeout "${DOCKER_TIMEOUT:-30}" docker stop "$cname" >/dev/null 2>&1 || true + _PM_TRAP_STOPPED+=("$cname") timeout "${DOCKER_TIMEOUT:-30}" docker rm "$cname" >/dev/null 2>&1 && \ log " $cname removed ✅" || warn " $cname rm failed" else @@ -428,7 +431,7 @@ if [[ "$AM_MIRROR" == true ]]; then if [[ "$DRY_RUN" == false ]]; then write_state_file "$LOCAL_STATE_FILE" \ "INACTIVE" "" "$NOW" "$LOCAL_SERVER_NAME" "$REASON" - log "Local state: INACTIVE ✅" + echo "Local state: INACTIVE ✅" add_to_blocklist "$OWNER" "$REASON" else warn "DRY RUN — would write INACTIVE state and blocklist $OWNER" @@ -626,7 +629,7 @@ fi if [[ ${#PARTNERSHIP_MIRROR_BACKUPS[@]} -gt 0 ]]; then echo "" echo "━━━ $ICON_DISK Backup Handover ━━━" - log "Backups available for $MIRROR:" + echo "Backups available for $MIRROR:" for path in "${PARTNERSHIP_MIRROR_BACKUPS[@]}"; do [[ -z "$path" ]] && continue echo " $path" diff --git a/Partnership/partnership_onboard.sh b/Partnership/partnership_onboard.sh index fc9c334..178a936 100755 --- a/Partnership/partnership_onboard.sh +++ b/Partnership/partnership_onboard.sh @@ -553,7 +553,7 @@ else deploy_xml_stack PARTNERSHIP_AUTH_STACK AUTH_DEPLOYED=$_STACK_DEPLOYED AUTH_FAILED=$_STACK_FAILED - log "Auth stack: $AUTH_DEPLOYED deployed, $AUTH_FAILED failed" + echo "Auth stack: $AUTH_DEPLOYED deployed, $AUTH_FAILED failed" [[ "$AUTH_FAILED" -gt 0 ]] && STEP_AUTH_OK=false fi @@ -580,7 +580,7 @@ else deploy_xml_stack PARTNERSHIP_ARR_STACK ARR_DEPLOYED=$_STACK_DEPLOYED ARR_FAILED=$_STACK_FAILED - log "Arr stack: $ARR_DEPLOYED deployed, $ARR_FAILED failed" + echo "Arr stack: $ARR_DEPLOYED deployed, $ARR_FAILED failed" [[ "$ARR_FAILED" -gt 0 ]] && STEP_ARR_OK=false fi @@ -589,7 +589,7 @@ echo "" echo "━━━ Step 7/8 — Partnership Onboard ━━━" if bash "$SCRIPTS_ROOT/Partnership/partnership_manager.sh" --onboard "${EXTRA_FLAGS[@]}"; then - log "Partnership onboard complete ✅" + echo "Partnership onboard complete ✅" ONBOARD_OK=true else error "Partnership onboard failed" @@ -607,7 +607,7 @@ elif [[ "$SKIP_ARR_SYNC" == true ]]; then elif [[ ! -f "$SCRIPTS_ROOT/Media/arr_sync.sh" ]]; then warn "arr_sync.sh not found — run Media/arr_sync.sh manually once arrs are live" elif bash "$SCRIPTS_ROOT/Media/arr_sync.sh" "${EXTRA_FLAGS[@]}"; then - log "Arr bootstrap complete ✅" + echo "Arr bootstrap complete ✅" ARR_SYNC_OK=true else warn "Arr sync had errors — partnership still valid" @@ -637,8 +637,8 @@ echo "" if [[ "$ONBOARD_OK" == true ]]; then [[ "$DRY_RUN" == true ]] && warn "DRY RUN — no changes made" || \ - log "$ICON_DONE DONE — partnership established ✅" - log "Verify with: Partnership/partnership_manager.sh --status" + echo "$ICON_DONE DONE — partnership established ✅" + echo "Verify with: Partnership/partnership_manager.sh --status" else error "Setup incomplete — resolve errors above and re-run" fi diff --git a/Partnership/ssh_setup.sh b/Partnership/ssh_setup.sh index 5d8551f..134454a 100755 --- a/Partnership/ssh_setup.sh +++ b/Partnership/ssh_setup.sh @@ -290,8 +290,8 @@ echo "━━━ $ICON_GEAR Key Generation ━━━" if [[ -f "$SSH_KEY_PATH" ]] && [[ "$FORCE" == false ]]; then local_fp=$(ssh-keygen -lf "$SSH_KEY_PATH" 2>/dev/null || echo "unreadable") - log "Key already exists — skipping generation (--force to regenerate)" - log " $local_fp" + echo "Key already exists — skipping generation (--force to regenerate)" + echo " $local_fp" else if [[ "$FORCE" == true ]] && [[ -f "$SSH_KEY_PATH" ]]; then warn "Regenerating key (--force) — existing key will be replaced" @@ -339,7 +339,7 @@ else if ssh-copy-id -i "$SSH_PUB_PATH" -o ConnectTimeout="${SSH_TIMEOUT:-15}" \ root@"$REMOTE_SERVER" 2>/dev/null; then - log "Public key installed on $REMOTE_SERVER_NAME ✅" + echo "Public key installed on $REMOTE_SERVER_NAME ✅" else error "ssh-copy-id failed — check that:" error " 1. Remote server is reachable: tailscale status" @@ -355,7 +355,7 @@ echo "━━━ $ICON_VERIFY Verify SSH Auth ━━━" if [[ "$DRY_RUN" == false ]]; then if test_ssh_auth "$REMOTE_SERVER"; then - log "SSH auth to $REMOTE_SERVER_NAME working ✅" + echo "SSH auth to $REMOTE_SERVER_NAME working ✅" # Reset any existing strikes if [[ -f "$SSH_STRIKE_FILE" ]]; then write_strike_file 0 "" "$(date '+%Y-%m-%d %H:%M:%S')"