diff --git a/docs/Tasktracking/2026-04-22-fix-migrate-blocking-and-uninstall-detection.md b/docs/Tasktracking/2026-04-22-fix-migrate-blocking-and-uninstall-detection.md new file mode 100644 index 00000000..ba6852e1 --- /dev/null +++ b/docs/Tasktracking/2026-04-22-fix-migrate-blocking-and-uninstall-detection.md @@ -0,0 +1,52 @@ +Task Record: + +Date: 2026-04-22 +Related Module: install/uninstall scripts (`install.sh`, `x-ui.sh`) +Change Type: Fix + +Background + +Installation could get stuck or fail at `x-ui migrate` before service installation completed. +When this happened, service files were not installed, but files under `/usr/local/x-ui` or `/etc/x-ui` could already exist. +Then `x-ui.sh uninstall` might reject uninstall with "please install first" because installation detection relied only on service file presence. + +Changes + +In `install.sh`, changed migration call to non-blocking behavior: +- Use `timeout 30` when available for `${xui_folder}/x-ui migrate`. +- If migration times out or fails, print warning and continue installation. +- Keep manual migration command hint for follow-up. + +In `x-ui.sh`, improved install detection in `check_status`: +- If service/init file is missing but residual install artifacts exist (`${xui_folder}/x-ui`, `${xui_folder}`, or `/etc/x-ui`), treat it as installed-but-not-running instead of not installed. +- This allows `x-ui uninstall` to proceed and clean residual files. + +Added `tests/install_uninstall_resilience_test.sh` for static regression checks of the new logic. + +Impact + +Affected files: +- `install.sh` +- `x-ui.sh` +- `tests/install_uninstall_resilience_test.sh` + +No API or database schema changes. +Installer runtime behavior is more resilient when migration has connectivity issues. +Uninstall command now works for partial/failed installation residue. + +Verification + +Commands: +- `bash -n install.sh` +- `bash -n x-ui.sh` +- `bash tests/install_uninstall_resilience_test.sh` +- `bash tests/panel_port_prompt_test.sh` +- `bash tests/mariadb_install_switch_test.sh` + +Result: +- All commands passed. + +Risks And Follow-Up + +Migration failure is now non-blocking, so some environments may finish install while still requiring manual migration. +For complete runtime coverage, a pty-driven install E2E test with unreachable MariaDB simulation can be added later. diff --git a/install.sh b/install.sh index 9b419932..a26daa1c 100644 --- a/install.sh +++ b/install.sh @@ -1312,7 +1312,15 @@ config_after_install() { echo -e "${green}访问地址:https://${final_host}:${existing_port}/${config_webBasePath}${plain}" fi - ${xui_folder}/x-ui migrate + if command -v timeout >/dev/null 2>&1; then + if ! timeout 30 ${xui_folder}/x-ui migrate; then + echo -e "${yellow}数据库迁移未在 30 秒内完成或执行失败,已跳过阻塞,安装继续。${plain}" + echo -e "${yellow}可在安装后手动执行:${xui_folder}/x-ui migrate${plain}" + fi + elif ! ${xui_folder}/x-ui migrate; then + echo -e "${yellow}数据库迁移执行失败,安装继续。${plain}" + echo -e "${yellow}可在安装后手动执行:${xui_folder}/x-ui migrate${plain}" + fi } get_releases() { diff --git a/tests/install_uninstall_resilience_test.sh b/tests/install_uninstall_resilience_test.sh new file mode 100644 index 00000000..76585916 --- /dev/null +++ b/tests/install_uninstall_resilience_test.sh @@ -0,0 +1,17 @@ +#!/usr/bin/env bash +set -euo pipefail + +assert_contains() { + local file="$1" + local pattern="$2" + if ! grep -Fq "$pattern" "$file"; then + echo "missing pattern in $file: $pattern" >&2 + return 1 + fi +} + +assert_contains "install.sh" "timeout 30 \${xui_folder}/x-ui migrate" +assert_contains "install.sh" "数据库迁移未在 30 秒内完成或执行失败" +assert_contains "x-ui.sh" "if [[ -x \"\${xui_folder}/x-ui\" || -d /etc/x-ui || -d \"\${xui_folder}\" ]]; then" + +echo "install/uninstall resilience checks look correct" diff --git a/x-ui.sh b/x-ui.sh index 5ba8866b..8e1bfea6 100644 --- a/x-ui.sh +++ b/x-ui.sh @@ -769,6 +769,9 @@ update_shell() { check_status() { if [[ $release == "alpine" ]]; then if [[ ! -f /etc/init.d/x-ui ]]; then + if [[ -x "${xui_folder}/x-ui" || -d /etc/x-ui || -d "${xui_folder}" ]]; then + return 1 + fi return 2 fi if [[ $(rc-service x-ui status | grep -F 'status: started' -c) == 1 ]]; then @@ -778,6 +781,9 @@ check_status() { fi else if [[ ! -f ${xui_service}/x-ui.service ]]; then + if [[ -x "${xui_folder}/x-ui" || -d /etc/x-ui || -d "${xui_folder}" ]]; then + return 1 + fi return 2 fi temp=$(systemctl status x-ui | grep Active | awk '{print $3}' | cut -d "(" -f2 | cut -d ")" -f1)