fix(update): improve updater script reliability for 1.7.0

- quote env vars in cmd script for paths with spaces

- add update_error.log diagnostics and timeout while waiting for app exit

- bump APP_VERSION to 1.7.0 and update updater tests
This commit is contained in:
2026-02-15 20:38:24 +03:00
parent e1e2f8f0e8
commit a9a394cf7d
3 changed files with 30 additions and 15 deletions

View File

@@ -1 +1 @@
APP_VERSION = "1.6.4" APP_VERSION = "1.7.0"

View File

@@ -86,39 +86,53 @@ class AutoUpdateService:
script_path = os.path.join(tempfile.gettempdir(), "anabasis_apply_update.cmd") script_path = os.path.join(tempfile.gettempdir(), "anabasis_apply_update.cmd")
script_lines = [ script_lines = [
"@echo off", "@echo off",
"setlocal", "setlocal EnableExtensions",
f"set APP_DIR={app_dir}", f"set \"APP_DIR={app_dir}\"",
f"set SRC_DIR={source_dir}", f"set \"SRC_DIR={source_dir}\"",
f"set EXE_NAME={exe_name}", f"set \"EXE_NAME={exe_name}\"",
f"set TARGET_PID={target_pid}", f"set \"TARGET_PID={target_pid}\"",
"set BACKUP_DIR=%TEMP%\\anabasis_backup_%RANDOM%%RANDOM%", "set \"BACKUP_DIR=%TEMP%\\anabasis_backup_%RANDOM%%RANDOM%\"",
"set \"UPDATE_LOG=%APP_DIR%\\update_error.log\"",
"echo [%DATE% %TIME%] Update start > \"%UPDATE_LOG%\"",
"if not exist \"%SRC_DIR%\\%EXE_NAME%\" (",
" echo Source executable not found: \"%SRC_DIR%\\%EXE_NAME%\" >> \"%UPDATE_LOG%\"",
" exit /b 3",
")",
"set /a WAIT_LOOPS=0",
":wait_for_exit", ":wait_for_exit",
"tasklist /FI \"PID eq %TARGET_PID%\" | find \"%TARGET_PID%\" >nul", "tasklist /FI \"PID eq %TARGET_PID%\" | find \"%TARGET_PID%\" >nul",
"if %ERRORLEVEL% EQU 0 (", "if %ERRORLEVEL% EQU 0 (",
" set /a WAIT_LOOPS+=1",
" if %WAIT_LOOPS% GEQ 180 (",
" echo Timeout waiting for process %TARGET_PID% to exit >> \"%UPDATE_LOG%\"",
" goto :backup",
" )",
" timeout /t 1 /nobreak >nul", " timeout /t 1 /nobreak >nul",
" goto :wait_for_exit", " goto :wait_for_exit",
")", ")",
":backup",
"timeout /t 1 /nobreak >nul", "timeout /t 1 /nobreak >nul",
"mkdir \"%BACKUP_DIR%\" >nul 2>&1", "mkdir \"%BACKUP_DIR%\" >nul 2>&1",
"robocopy \"%APP_DIR%\" \"%BACKUP_DIR%\" /E /NFL /NDL /NJH /NJS /NP /R:6 /W:2 >nul", "robocopy \"%APP_DIR%\" \"%BACKUP_DIR%\" /E /NFL /NDL /NJH /NJS /NP /R:6 /W:2 >nul",
"set RC=%ERRORLEVEL%", "set \"RC=%ERRORLEVEL%\"",
"if %RC% GEQ 8 goto :backup_error", "if %RC% GEQ 8 goto :backup_error",
"robocopy \"%SRC_DIR%\" \"%APP_DIR%\" /E /NFL /NDL /NJH /NJS /NP /R:12 /W:2 >nul", "robocopy \"%SRC_DIR%\" \"%APP_DIR%\" /E /NFL /NDL /NJH /NJS /NP /R:12 /W:2 >nul",
"set RC=%ERRORLEVEL%", "set \"RC=%ERRORLEVEL%\"",
"if %RC% GEQ 8 goto :rollback", "if %RC% GEQ 8 goto :rollback",
"start \"\" \"%APP_DIR%\\%EXE_NAME%\"", "start \"\" \"%APP_DIR%\\%EXE_NAME%\"",
"timeout /t 2 /nobreak >nul", "timeout /t 2 /nobreak >nul",
"tasklist /FI \"IMAGENAME eq %EXE_NAME%\" | find /I \"%EXE_NAME%\" >nul", "tasklist /FI \"IMAGENAME eq %EXE_NAME%\" | find /I \"%EXE_NAME%\" >nul",
"if %ERRORLEVEL% NEQ 0 goto :rollback", "if %ERRORLEVEL% NEQ 0 goto :rollback",
"echo Update success >> \"%UPDATE_LOG%\"",
"rmdir /S /Q \"%BACKUP_DIR%\" >nul 2>&1", "rmdir /S /Q \"%BACKUP_DIR%\" >nul 2>&1",
"exit /b 0", "exit /b 0",
":rollback", ":rollback",
"robocopy \"%BACKUP_DIR%\" \"%APP_DIR%\" /E /NFL /NDL /NJH /NJS /NP /R:6 /W:2 >nul", "robocopy \"%BACKUP_DIR%\" \"%APP_DIR%\" /E /NFL /NDL /NJH /NJS /NP /R:6 /W:2 >nul",
"start \"\" \"%APP_DIR%\\%EXE_NAME%\"", "start \"\" \"%APP_DIR%\\%EXE_NAME%\"",
"echo Auto-update failed. Rollback executed. > \"%APP_DIR%\\update_error.log\"", "echo Auto-update failed. Rollback executed. >> \"%UPDATE_LOG%\"",
"exit /b 2", "exit /b 2",
":backup_error", ":backup_error",
"echo Auto-update failed during backup. Code %RC% > \"%APP_DIR%\\update_error.log\"", "echo Auto-update failed during backup. Code %RC% >> \"%UPDATE_LOG%\"",
"exit /b %RC%", "exit /b %RC%",
] ]
with open(script_path, "w", encoding="utf-8", newline="\r\n") as f: with open(script_path, "w", encoding="utf-8", newline="\r\n") as f:

View File

@@ -39,11 +39,12 @@ class AutoUpdateServiceTests(unittest.TestCase):
target_pid=1234, target_pid=1234,
) )
script_text = Path(script).read_text(encoding="utf-8") script_text = Path(script).read_text(encoding="utf-8")
self.assertIn("set APP_DIR=", script_text) self.assertIn("set \"APP_DIR=", script_text)
self.assertIn("set SRC_DIR=", script_text) self.assertIn("set \"SRC_DIR=", script_text)
self.assertIn("set EXE_NAME=", script_text) self.assertIn("set \"EXE_NAME=", script_text)
self.assertIn("set TARGET_PID=", script_text) self.assertIn("set \"TARGET_PID=", script_text)
self.assertIn(":rollback", script_text) self.assertIn(":rollback", script_text)
self.assertIn("if not exist \"%SRC_DIR%\\%EXE_NAME%\"", script_text)
if __name__ == "__main__": if __name__ == "__main__":