diff --git a/core/core.func b/core/core.func index 8af1637..a945321 100644 --- a/core/core.func +++ b/core/core.func @@ -1031,6 +1031,26 @@ stop_spinner() { stty sane 2>/dev/null || true } +# A helper opening its own block (setup_uv, setup_nodejs) stopped the outer +# one's spinner and nothing brought it back. +declare -ga _MSG_BLOCKS=() + +_msg_block_resume() { + ((${#_MSG_BLOCKS[@]})) || return 0 + SPINNER_MSG="${_MSG_BLOCKS[-1]}" + + if is_verbose_mode || is_alpine; then + printf "\r\e[2K%s %b" "${TAB}⏳${TAB}" "${YW}${SPINNER_MSG}${CL}" >&2 + return 0 + fi + + color_spinner + spinner & + SPINNER_PID=$! + echo "$SPINNER_PID" >/tmp/.spinner.pid + disown "$SPINNER_PID" 2>/dev/null || true +} + # ------------------------------------------------------------------------------ # _cs_os_family() # @@ -1151,7 +1171,10 @@ msg_info() { fi local sanitized_key sanitized_key=$(printf '%s' "$msg" | sed 's/\x1b\[[0-9;]*m//g; s/[^a-zA-Z0-9_]/_/g') - [[ -n "${MSG_INFO_SHOWN["$sanitized_key"]+x}" ]] && return + if [[ -n "${MSG_INFO_SHOWN["$sanitized_key"]+x}" ]]; then + _MSG_BLOCKS+=("$msg") + return + fi MSG_INFO_SHOWN["$sanitized_key"]=1 _dev_step_start "$msg" @@ -1161,6 +1184,7 @@ msg_info() { stop_spinner SPINNER_MSG="$msg" + _MSG_BLOCKS+=("$msg") if is_verbose_mode || is_alpine; then local HOURGLASS="${TAB}⏳${TAB}" @@ -1200,12 +1224,14 @@ msg_ok() { local msg="$1" [[ -z "$msg" ]] && return stop_spinner + ((${#_MSG_BLOCKS[@]})) && unset '_MSG_BLOCKS[-1]' clear_line echo -e "$CM${GN}${msg}${CL}$(_dev_step_end "$msg")" log_msg "[OK] $msg" local sanitized_key sanitized_key=$(printf '%s' "$msg" | sed 's/\x1b\[[0-9;]*m//g; s/[^a-zA-Z0-9_]/_/g') unset 'MSG_INFO_SHOWN['"$sanitized_key"']' 2>/dev/null || true + _msg_block_resume } # ------------------------------------------------------------------------------ @@ -1218,6 +1244,7 @@ msg_ok() { # ------------------------------------------------------------------------------ msg_error() { stop_spinner + _MSG_BLOCKS=() local msg="$1" echo -e "${BFR:-}${CROSS:-✖️}${RD}${msg}${CL}" >&2 log_msg "[ERROR] $msg" @@ -1233,6 +1260,7 @@ msg_error() { # ------------------------------------------------------------------------------ msg_warn() { stop_spinner + _MSG_BLOCKS=() local msg="$1" echo -e "${BFR:-}${INFO:-ℹ️} ${YWB}${msg}${CL}" >&2 log_msg "[WARN] $msg" @@ -1255,6 +1283,7 @@ msg_custom() { # Align with CM/CROSS: TAB + icon + TAB + message (not a single leading space). echo -e "${BFR:-}${TAB:- }${symbol}${TAB:- }${color}${msg}${CL:-\e[0m}" log_msg "$msg" + _msg_block_resume } # ------------------------------------------------------------------------------ @@ -2043,6 +2072,16 @@ prompt_select() { # # Require user input (no default) # db_pass=$(prompt_password "Database password:" "" 60 12) # ------------------------------------------------------------------------------ +# random_password [length] - alphanumeric, because base64 carries / and +. +# Reads a fixed chunk rather than piping urandom into head, which SIGPIPEs. +random_password() { + local len="${1:-24}" out="" + while ((${#out} < len)); do + out+="$(LC_ALL=C tr -dc 'A-Za-z0-9' < <(head -c 256 /dev/urandom))" + done + printf '%s' "${out:0:len}" +} + prompt_password() { local message="${1:-Enter password:}" local default="${2:-}" @@ -2248,7 +2287,7 @@ check_or_create_swap() { msg_error "No active swap detected" if ! prompt_confirm "Do you want to create a swap file?" "n" 60; then - msg_info "Skipping swap file creation" + msg_custom "ℹ️" "${YW}" "Skipping swap file creation" return 1 fi diff --git a/lib/API.txt b/lib/API.txt index 7aa63f9..639b58c 100644 --- a/lib/API.txt +++ b/lib/API.txt @@ -5,6 +5,7 @@ _cs_apt_install_optional _cs_clean_target_dir _cs_drop_pm_shim _cs_repair_dpkg +_cs_write_cargo_net _cs_write_cargo_profile _deploy_source_tarball _deploy_unpacked_archive diff --git a/lib/alpine.func b/lib/alpine.func index 094791d..6a9c73c 100644 --- a/lib/alpine.func +++ b/lib/alpine.func @@ -337,14 +337,14 @@ check_for_gh_release() { msg_ok "$app pinned v$pinned installed (upstream v$release)" return 1 fi - msg_info "$app pinned v$pinned (upstream v$release) → update/downgrade" + msg_ok "$app pinned v$pinned (upstream v$release) → update/downgrade" CHECK_UPDATE_RELEASE="$pinned" return 0 fi if [ "$release" != "$current" ] || [ ! -f "$current_file" ]; then CHECK_UPDATE_RELEASE="$release" - msg_info "New release available: v$release (current: v${current:-none})" + msg_ok "New release available: v$release (current: v${current:-none})" return 0 fi diff --git a/lib/db.func b/lib/db.func index 5fef663..b991f74 100644 --- a/lib/db.func +++ b/lib/db.func @@ -182,7 +182,7 @@ EOF local HAD_MARIADB_REPO=false if [[ -f /etc/apt/sources.list.d/mariadb.sources ]] || [[ -f /etc/apt/sources.list.d/mariadb.list ]]; then HAD_MARIADB_REPO=true - msg_info "Removing MariaDB official repository (switching to distribution packages)" + msg_custom "ℹ️" "${YW}" "Removing MariaDB official repository (switching to distribution packages)" fi # Clean up any existing MariaDB repository files to avoid conflicts @@ -250,7 +250,7 @@ EOF # Check if old repo points to a different version if [[ -n "$OLD_REPO_VERSION" ]] && [[ "${OLD_REPO_VERSION%.*}" != "${MARIADB_VERSION%.*}" ]]; then - msg_info "Cleaning up old MariaDB repository (was: $OLD_REPO_VERSION, requested: $MARIADB_VERSION)" + msg_custom "ℹ️" "${YW}" "Cleaning up old MariaDB repository (was: $OLD_REPO_VERSION, requested: $MARIADB_VERSION)" cleanup_old_repo_files "mariadb" $STD apt update || msg_warn "APT update had issues, continuing..." fi @@ -570,7 +570,7 @@ setup_meilisearch() { local NEEDS_MIGRATION=false if [[ "$CURRENT_MAJOR_MINOR" != "$NEW_MAJOR_MINOR" ]]; then NEEDS_MIGRATION=true - msg_info "MeiliSearch version change detected (${CURRENT_VERSION} → ${NEW_VERSION}), preparing data migration" + msg_custom "ℹ️" "${YW}" "MeiliSearch version change detected (${CURRENT_VERSION} → ${NEW_VERSION}), preparing data migration" fi # Read connection + storage paths for the in-place (dumpless) upgrade @@ -1258,6 +1258,7 @@ setup_postgresql() { msg_error "Failed to backup PostgreSQL databases" return 150 } + msg_ok "Backed up PostgreSQL ${CURRENT_PG_VERSION} databases to ${PG_BACKUP_FILE}" $STD systemctl stop postgresql || true $STD apt purge -y "postgresql-${CURRENT_PG_VERSION}" "postgresql-client-${CURRENT_PG_VERSION}" 2>/dev/null || true else diff --git a/lib/forge.func b/lib/forge.func index 7e79eab..e4b36ac 100644 --- a/lib/forge.func +++ b/lib/forge.func @@ -470,21 +470,45 @@ _download_source_tarball() { # directory" for entries deleted on purpose. A file rather than an array, # because node_modules reaches tens of thousands of paths. _cs_clean_target_dir() { - local dir="${1:?directory}" list + local dir="${1:?directory}" list entry stash="" [[ -d "$dir" ]] || return 0 + dir="${dir%/}" + + # Moved aside rather than pruned from find: a kept path can be nested, and + # removing its parent would take it along. mv also keeps a symlink a symlink. + local -a keep=() + [[ -n "${CLEAN_INSTALL_KEEP:-}" ]] && read -ra keep <<<"$CLEAN_INSTALL_KEEP" + if ((${#keep[@]})); then + stash="$(mktemp -d -p "$(dirname "$dir")" .clean-keep.XXXXXX)" || return 1 + for entry in "${keep[@]}"; do + entry="${entry#/}" + [[ -n "$entry" && -e "$dir/$entry" ]] || continue + mkdir -p "$stash/$(dirname "$entry")" + mv "$dir/$entry" "$stash/$entry" || msg_warn "Could not preserve ${entry}" + done + fi # mountpoint lives in util-linux, which a minimal Alpine may not have. Without # it the -exec below would fail once per directory, and there is nothing to # protect anyway -- so drop the top-level entries and let rm recurse. if ! command -v mountpoint >/dev/null 2>&1; then find "${dir:?}" -mindepth 1 -maxdepth 1 -print0 | xargs -0 rm -rf -- - return 0 + else + list="$(mktemp)" || return 1 + find "${dir:?}" -mindepth 1 \( -type d -exec mountpoint -q {} \; -prune \) -o -print0 >"$list" + xargs -0 rm -rf -- <"$list" + rm -f "$list" + fi + + if [[ -n "$stash" ]]; then + for entry in "${keep[@]}"; do + entry="${entry#/}" + [[ -n "$entry" && -e "$stash/$entry" ]] || continue + mkdir -p "$dir/$(dirname "$entry")" + mv "$stash/$entry" "$dir/$entry" || msg_warn "Could not restore ${entry}" + done + rm -rf "$stash" fi - - list="$(mktemp)" || return 1 - find "${dir:?}" -mindepth 1 \( -type d -exec mountpoint -q {} \; -prune \) -o -print0 >"$list" - xargs -0 rm -rf -- <"$list" - rm -f "$list" } # ------------------------------------------------------------------------------ @@ -498,6 +522,8 @@ _cs_clean_target_dir() { # - Honors CLEAN_INSTALL=1 (wipes first, dotfiles included, mount # points preserved — back up config dotfiles like .env via create_backup # and call restore_backup BEFORE any build step that sources them). +# - Honors CLEAN_INSTALL_KEEP, a space-separated list of paths relative to +# that the wipe leaves alone (e.g. "geodata .env data/uploads"). # - Does NOT own : the caller creates it and is responsible for its # cleanup (typically via a RETURN trap on its tmpdir). # - cp failures are non-fatal here, matching the previous inline behavior. @@ -543,6 +569,8 @@ _deploy_source_tarball() { # - Honors CLEAN_INSTALL=1 (wipes first, dotfiles included, mount # points preserved — back up config dotfiles like .env via create_backup # and call restore_backup BEFORE any build step that sources them). +# - Honors CLEAN_INSTALL_KEEP, a space-separated list of paths relative to +# that the wipe leaves alone (e.g. "geodata .env data/uploads"). # - Does NOT own : the caller creates it and cleans it up. # # Returns: 0 on success, 65 on unsupported format, 251 on extraction failure, @@ -2222,6 +2250,7 @@ fetch_and_deploy_from_url() { msg_error "Download failed: $url" return 250 } + msg_ok "Downloaded ${filename}" # Auto-detect archive type using file description local file_desc @@ -2248,7 +2277,7 @@ fetch_and_deploy_from_url() { return 65 fi - msg_info "Detected archive type: $archive_type (file type: $file_desc)" + debug_log "Detected archive type: $archive_type (file type: $file_desc)" if [[ "$archive_type" == "deb" ]]; then msg_info "Installing .deb package" diff --git a/lib/hwaccel.func b/lib/hwaccel.func index 587ce9b..71783aa 100644 --- a/lib/hwaccel.func +++ b/lib/hwaccel.func @@ -198,7 +198,7 @@ setup_hwaccel() { selection="${selection^^}" # uppercase else echo "" - msg_info "Timeout - configuring all GPUs automatically" + msg_custom "ℹ️" "${YW}" "Timeout - configuring all GPUs automatically" selection="A" fi @@ -391,7 +391,7 @@ _setup_intel_arc() { # For Trixie/Sid: Fetch latest drivers from GitHub (Debian repo packages may be too old or missing) # For Bookworm: Use repo packages (GitHub latest requires libstdc++6 >= 13.1, unavailable on Bookworm) if [[ "$os_codename" == "trixie" || "$os_codename" == "sid" ]]; then - msg_info "Fetching Intel compute-runtime from GitHub for Arc support" + msg_custom "ℹ️" "${YW}" "Fetching Intel compute-runtime from GitHub for Arc support" # intel-opencl-icd declares it; dpkg refuses the package without it. _cs_apt_install_optional ocl-icd-libopencl1 @@ -449,7 +449,7 @@ _setup_intel_modern() { # For Trixie/Sid: Fetch from GitHub (Debian packages too old or missing) if [[ "$os_codename" == "trixie" || "$os_codename" == "sid" ]]; then - msg_info "Fetching Intel compute-runtime from GitHub" + msg_custom "ℹ️" "${YW}" "Fetching Intel compute-runtime from GitHub" # intel-opencl-icd declares it; dpkg refuses the package without it. _cs_apt_install_optional ocl-icd-libopencl1 @@ -684,7 +684,7 @@ _setup_nvidia_gpu() { return 0 fi - msg_info "Host NVIDIA driver version: ${nvidia_host_version}" + msg_custom "ℹ️" "${YW}" "Host NVIDIA driver version: ${nvidia_host_version}" if [[ "$os_id" == "debian" ]]; then # Enable non-free components @@ -728,6 +728,7 @@ _setup_nvidia_gpu() { cuda_keyring="$(mktemp)" if curl -fsSL -o "$cuda_keyring" "https://developer.download.nvidia.com/compute/cuda/repos/${cuda_repos[0]}/x86_64/cuda-keyring_1.1-1_all.deb" 2>/dev/null; then $STD dpkg -i "$cuda_keyring" 2>/dev/null || true + msg_ok "Added NVIDIA CUDA repository keyring" else msg_warn "Failed to download NVIDIA CUDA keyring" fi @@ -842,6 +843,7 @@ NVIDIA_PIN cuda_keyring="$(mktemp)" if curl -fsSL -o "$cuda_keyring" "https://developer.download.nvidia.com/compute/cuda/repos/${ubuntu_cuda_repo}/x86_64/cuda-keyring_1.1-1_all.deb" 2>/dev/null; then $STD dpkg -i "$cuda_keyring" 2>/dev/null || true + msg_ok "Added NVIDIA CUDA repository (${ubuntu_cuda_repo})" else msg_warn "Failed to download NVIDIA CUDA keyring" fi @@ -900,7 +902,7 @@ NVIDIA_PIN if command -v update-glx &>/dev/null; then local nvidia_glx_dir="/usr/lib/nvidia" if [[ ! -f "${nvidia_glx_dir}/libGL.so.1" ]] && [[ -d /usr/lib/mesa-diverted ]]; then - msg_info "NVIDIA GLX libs missing in container - falling back to mesa" + msg_custom "ℹ️" "${YW}" "NVIDIA GLX libs missing in container - falling back to mesa" $STD update-glx --set glx /usr/lib/mesa-diverted 2>/dev/null || true ldconfig 2>/dev/null || true fi @@ -1083,7 +1085,7 @@ _setup_gpu_permissions() { # Verify VA-API if command -v vainfo &>/dev/null; then if vainfo &>/dev/null; then - msg_info "VA-API verified and working" + msg_custom "✔️" "${GN}" "VA-API verified and working" else msg_warn "vainfo test failed - check GPU passthrough" fi @@ -1109,11 +1111,12 @@ _setup_gpu_permissions() { } done fi + msg_ok "Configured NVIDIA device permissions" # Verify nvidia-smi if command -v nvidia-smi &>/dev/null; then if nvidia-smi &>/dev/null; then - msg_info "nvidia-smi verified and working" + msg_custom "✔️" "${GN}" "nvidia-smi verified and working" else msg_warn "nvidia-smi test failed - check driver version match" fi @@ -1124,7 +1127,7 @@ _setup_gpu_permissions() { if [[ -e /dev/kfd ]]; then chgrp render /dev/kfd 2>/dev/null || true chmod 660 /dev/kfd 2>/dev/null || true - msg_info "AMD ROCm compute device configured" + msg_custom "✔️" "${GN}" "AMD ROCm compute device configured" fi # Add service user to render and video groups for GPU hardware acceleration diff --git a/lib/runtime.func b/lib/runtime.func index 92b73ac..6cf7308 100644 --- a/lib/runtime.func +++ b/lib/runtime.func @@ -71,6 +71,7 @@ setup_composer() { $STD "$COMPOSER_BIN" self-update --no-interaction || { msg_warn "Composer self-update failed, continuing with current version" } + "$COMPOSER_BIN" config --global process-timeout 600 >/dev/null 2>&1 || true local UPDATED_VERSION UPDATED_VERSION=$("$COMPOSER_BIN" --version 2>/dev/null | awk '{print $3}') cache_installed_version "composer" "$UPDATED_VERSION" @@ -107,6 +108,7 @@ setup_composer() { fi chmod +x "$COMPOSER_BIN" + "$COMPOSER_BIN" config --global process-timeout 600 >/dev/null 2>&1 || true $STD "$COMPOSER_BIN" self-update --no-interaction || { msg_warn "Composer self-update failed after fresh install" } @@ -226,13 +228,13 @@ setup_docker() { if command -v docker &>/dev/null; then docker_installed=true DOCKER_CURRENT_VERSION=$(docker --version | grep -oP '\d+\.\d+\.\d+' | head -1) - msg_info "Docker $DOCKER_CURRENT_VERSION detected" + msg_custom "ℹ️" "${YW}" "Docker $DOCKER_CURRENT_VERSION detected" fi # Check if Portainer is running if docker ps --format '{{.Names}}' 2>/dev/null | grep -q '^portainer$'; then portainer_installed=true - msg_info "Portainer container detected" + msg_custom "ℹ️" "${YW}" "Portainer container detected" fi # Scenario 1: Use distro repository (opt-out via USE_DOCKER_REPO=false) @@ -285,6 +287,7 @@ EOF msg_info "Migrating from old Docker repository format" rm -f /etc/apt/sources.list.d/docker.list rm -f /etc/apt/keyrings/docker.asc + msg_ok "Migrated from old Docker repository format" fi # Setup/Update Docker repository @@ -296,6 +299,7 @@ EOF "$(get_os_info codename)" \ "stable" \ "$(dpkg --print-architecture)" + msg_ok "Set up Docker Repository" # Install or upgrade Docker if [ "$docker_installed" = true ]; then @@ -303,6 +307,7 @@ EOF DOCKER_LATEST_VERSION=$(apt-cache policy docker-ce | grep Candidate | awk '{print $2}' 2>/dev/null | cut -d':' -f2 | cut -d'-' -f1 || echo '') if [ "$DOCKER_CURRENT_VERSION" != "$DOCKER_LATEST_VERSION" ]; then + msg_ok "Docker update available: $DOCKER_CURRENT_VERSION → $DOCKER_LATEST_VERSION" msg_info "Updating Docker $DOCKER_CURRENT_VERSION → $DOCKER_LATEST_VERSION" $STD apt install -y --only-upgrade \ docker-ce \ @@ -356,8 +361,9 @@ EOF PORTAINER_LATEST=$(curl -fsSL https://registry.hub.docker.com/v2/repositories/portainer/portainer-ce/tags?page_size=100 | grep -oP '"name":"\K[0-9]+\.[0-9]+\.[0-9]+"' | head -1 | tr -d '"') if [ "$PORTAINER_CURRENT" != "$PORTAINER_LATEST" ]; then + msg_ok "Portainer update available: $PORTAINER_CURRENT → $PORTAINER_LATEST" if _docker_is_noninteractive; then - msg_info "Skipping Portainer update prompt (non-interactive)" + msg_custom "ℹ️" "${YW}" "Skipping Portainer update prompt (non-interactive)" else # Timeout like the container prompts further down. Without one, a # batch update over many LXCs stops dead on the first Portainer that @@ -434,12 +440,10 @@ EOF fi done < <(docker ps --format '{{.Names}} {{.Image}}') - # Stop spinner before any interactive prompt - stop_spinner - if [[ ${#compose_updates[@]} -eq 0 && ${#standalone_updates[@]} -eq 0 ]]; then msg_ok "All containers are up-to-date" else + msg_ok "Container updates available" local reply for entry in "${compose_updates[@]}"; do IFS='|' read -r name image compose_workdir compose_service <<<"$entry" @@ -1726,6 +1730,7 @@ setup_php() { stop_all_services "php.*-fpm" $STD apt purge -y "php*" 2>/dev/null || true $STD apt autoremove -y 2>/dev/null || true + msg_ok "Removed conflicting PHP ${CURRENT_PHP}" fi # NOW create pinning for the desired version @@ -1756,6 +1761,7 @@ EOF } # Don't use $STD for add-apt-repository as it uses background processes add-apt-repository -y ppa:ondrej/php >>"$(get_active_logfile)" 2>&1 + msg_ok "Added ondrej/php PPA" else # Debian: Use Sury repository manage_tool_repository "php" "$PHP_VERSION" "" "https://packages.sury.org/debsuryorg-archive-keyring.deb" || { @@ -1803,7 +1809,7 @@ EOF # Log skipped modules (informational, not an error) if [[ -n "$SKIPPED_MODULES" ]]; then - msg_info "Skipping unavailable/built-in modules: $SKIPPED_MODULES" + msg_custom "ℹ️" "${YW}" "Skipping unavailable/built-in modules: $SKIPPED_MODULES" fi if [[ "$PHP_FPM" == "YES" ]]; then @@ -1833,6 +1839,7 @@ EOF install_packages_with_retry "libapache2-mod-php${PHP_VERSION}" || { msg_warn "Failed to install libapache2-mod-php${PHP_VERSION}, continuing without Apache module" } + msg_ok "Installed Apache with PHP ${PHP_VERSION} module" fi fi @@ -2262,6 +2269,14 @@ EOF chmod +x /etc/profile.d/99-cargo.sh } +# Appended, not written, so an existing config survives. +_cs_write_cargo_net() { + local cfg="${CARGO_HOME:-${HOME:-/root}/.cargo}/config.toml" + grep -q '^\[net\]' "$cfg" 2>/dev/null && return 0 + mkdir -p "$(dirname "$cfg")" + printf '\n[net]\nretry = 5\n' >>"$cfg" +} + # A source tree with rust-toolchain.toml makes rustup install that exact # version on build and never remove it. Keep the default, drop the rest. rust_prune_toolchains() { @@ -2359,6 +2374,8 @@ setup_rust() { msg_ok "Update Rust $RUST_VERSION" fi + _cs_write_cargo_net + # Install global crates if [[ -n "$RUST_CRATES" ]]; then msg_info "Processing Rust crates: $RUST_CRATES" @@ -2493,6 +2510,8 @@ _uv_required_version() { # UV_PROJECT_DIR - optional project directory; its uv pin wins over the latest # release. Set as a prefix, like PYTHON_VERSION and UV_VERSION. setup_uv() { + # uv has no config key for this, only the env var. + export UV_HTTP_TIMEOUT="${UV_HTTP_TIMEOUT:-600}" local PROJECT_DIR="${UV_PROJECT_DIR:-}" local UV_BIN="/usr/local/bin/uv" local UVX_BIN="/usr/local/bin/uvx" diff --git a/pve/backend.func b/pve/backend.func index 70832ad..4800893 100644 --- a/pve/backend.func +++ b/pve/backend.func @@ -1522,7 +1522,7 @@ PROFILE echo -e " Network: ${NET:-dhcp} | Bridge: ${BRG:-vmbr0}" echo -e " Verbose: ${GN}enabled${CL}" echo "" - msg_info "Restarting installation..." + msg_custom "ℹ️" "${YW}" "Restarting installation..." declare -f telemetry_new_attempt &>/dev/null && telemetry_new_attempt build_container return $? @@ -1630,7 +1630,7 @@ PROFILE echo -e " Disk: ${DISK_SIZE} GB | Network: ${NET:-dhcp} | Bridge: ${BRG:-vmbr0}" echo -e " Verbose: ${GN}enabled${CL}" echo "" - msg_info "Restarting installation..." + msg_custom "ℹ️" "${YW}" "Restarting installation..." declare -f telemetry_new_attempt &>/dev/null && telemetry_new_attempt build_container return $? @@ -1659,7 +1659,7 @@ PROFILE echo -e " Network: ${NET:-dhcp} | Bridge: ${BRG:-vmbr0}" echo -e " Verbose: ${GN}enabled${CL}" echo "" - msg_info "Restarting installation..." + msg_custom "ℹ️" "${YW}" "Restarting installation..." declare -f telemetry_new_attempt &>/dev/null && telemetry_new_attempt build_container return $? @@ -1683,7 +1683,7 @@ PROFILE echo -e " DNS: ${GN}8.8.8.8, 1.1.1.1${CL} (inside LXC only)" echo -e " Verbose: ${GN}enabled${CL}" echo "" - msg_info "Restarting installation..." + msg_custom "ℹ️" "${YW}" "Restarting installation..." declare -f telemetry_new_attempt &>/dev/null && telemetry_new_attempt build_container return $? diff --git a/ui/API.txt b/ui/API.txt index e30d22f..8dd223c 100644 --- a/ui/API.txt +++ b/ui/API.txt @@ -37,6 +37,7 @@ _is_whitelisted_key _list_os_versions _list_templates _load_vars_file_to_map +_msg_block_resume _msg_fit _pve_version _pveam_available @@ -158,6 +159,7 @@ prompt_input_required prompt_password prompt_select pve_check +random_password reclaim_tty require_debian_like require_pve_host diff --git a/ui/menu.func b/ui/menu.func index 83d066c..fbe7f25 100644 --- a/ui/menu.func +++ b/ui/menu.func @@ -735,7 +735,7 @@ run_addon_updates() { ((${#addons[@]} == 0)) && return 0 if [[ "${PHS_SILENT:-0}" == "1" ]]; then - msg_info "Detected ${#addons[@]} addon update script(s) - skipping (PHS_SILENT)" + msg_custom "ℹ️" "${YW}" "Detected ${#addons[@]} addon update script(s) - skipping (PHS_SILENT)" return 0 fi @@ -756,7 +756,7 @@ run_addon_updates() { bash "$a" || msg_warn "Addon update for $name failed (rc=$?)" ;; *) - msg_info "Skipped addon: $name" + msg_custom "ℹ️" "${YW}" "Skipped addon: $name" ;; esac done @@ -828,11 +828,11 @@ runtime_script_status_guard() { [[ "$installed_clean" =~ ^v[0-9] ]] && installed_clean="${installed_clean:1}" if [[ "$installed_clean" == "$pinned_clean" ]]; then if [[ -n "$pin_reason" ]]; then - msg_info "You are already on the pinned version (${pinned_version}). ${pin_reason}" + msg_custom "ℹ️" "${YW}" "You are already on the pinned version (${pinned_version}). ${pin_reason}" else - msg_info "You are already on the pinned version (${pinned_version}). No newer update is offered intentionally — please do not open an issue unless you see an actual error." + msg_custom "ℹ️" "${YW}" "You are already on the pinned version (${pinned_version}). No newer update is offered intentionally — please do not open an issue unless you see an actual error." fi - msg_info "More info: ${info_url}" + msg_custom "ℹ️" "${YW}" "More info: ${info_url}" fi fi fi @@ -1161,7 +1161,7 @@ start() { set_std_mode _cs_run_update elif ! command -v whiptail &>/dev/null || ! [ -t 0 ] || [[ "$TERM" == "dumb" ]]; then - msg_info "No interactive terminal detected – defaulting to silent update mode" + msg_custom "ℹ️" "${YW}" "No interactive terminal detected – defaulting to silent update mode" VERBOSE="no" set_std_mode _cs_run_update