v1.8.93: rewrite update script from VBS+PowerShell to plain BAT with logging

VBS→PowerShell chain was silently failing. BAT is simpler, doesn't depend
on execution policy, and writes detailed log to %TEMP%\sm_update.log
for debugging.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
chrome-storm-c442
2026-03-02 04:13:36 -05:00
parent bc48aea1e4
commit 273666236a
3 changed files with 79 additions and 56 deletions

View File

@@ -262,75 +262,98 @@ class UpdateChecker:
return self._apply_linux(binary_path, current_exe) return self._apply_linux(binary_path, current_exe)
def _apply_windows(self, new_exe: str, current_exe: str) -> bool: def _apply_windows(self, new_exe: str, current_exe: str) -> bool:
"""Windows update: PowerShell script waits for exit, copies, launches.""" """Windows update: BAT script waits for exit, copies, launches."""
try: try:
tmp_dir = tempfile.gettempdir() tmp_dir = tempfile.gettempdir()
ps_path = os.path.join(tmp_dir, "sm_update.ps1") bat_path = os.path.join(tmp_dir, "sm_update.bat")
vbs_path = os.path.join(tmp_dir, "sm_update.vbs") log_path = os.path.join(tmp_dir, "sm_update.log")
pid = os.getpid() pid = os.getpid()
mei_dir = getattr(sys, '_MEIPASS', '')
exe_dir = os.path.dirname(current_exe)
ps_content = f""" bat_content = f"""@echo off
$pid = {pid} chcp 65001 >nul 2>&1
$src = '{new_exe}' set "LOGFILE={log_path}"
$dst = '{current_exe}' set "SRC={new_exe}"
$tempDir = '{tmp_dir}' set "DST={current_exe}"
set "PID={pid}"
set "TMPDIR={tmp_dir}"
# Wait for old process to exit echo [%date% %time%] Update script started >> "%LOGFILE%"
while (Get-Process -Id $pid -ErrorAction SilentlyContinue) {{ echo [%date% %time%] PID to wait for: %PID% >> "%LOGFILE%"
Start-Sleep -Seconds 1 echo [%date% %time%] SRC: %SRC% >> "%LOGFILE%"
}} echo [%date% %time%] DST: %DST% >> "%LOGFILE%"
Start-Sleep -Seconds 3
# Clean ALL stale _MEI directories (best-effort, skip locked) :wait_loop
try {{ tasklist /FI "PID eq %PID%" 2>nul | find "%PID%" >nul
Get-ChildItem -Path $tempDir -Directory -Filter '_MEI*' -ErrorAction SilentlyContinue | ForEach-Object {{ if %errorlevel%==0 (
Remove-Item -Path $_.FullName -Recurse -Force -ErrorAction SilentlyContinue echo [%date% %time%] Waiting for PID %PID% to exit... >> "%LOGFILE%"
}} timeout /t 1 /nobreak >nul
}} catch {{}} goto wait_loop
Start-Sleep -Seconds 1 )
# Copy with retry echo [%date% %time%] Process exited, waiting 3s... >> "%LOGFILE%"
$copied = $false timeout /t 3 /nobreak >nul
for ($i = 0; $i -lt 5; $i++) {{
try {{
Copy-Item -Path $src -Destination $dst -Force
$srcSize = (Get-Item $src).Length
$dstSize = (Get-Item $dst).Length
if ($srcSize -eq $dstSize) {{
$copied = $true
break
}}
}} catch {{}}
Start-Sleep -Seconds 2
}}
if (-not $copied) {{ exit 1 }} rem Clean stale _MEI directories
echo [%date% %time%] Cleaning _MEI directories... >> "%LOGFILE%"
for /d %%D in ("%TMPDIR%\\_MEI*") do (
rmdir /s /q "%%D" >nul 2>&1
)
timeout /t 1 /nobreak >nul
Start-Sleep -Seconds 2 rem Copy with retry
echo [%date% %time%] Starting copy... >> "%LOGFILE%"
set COPIED=0
for /L %%i in (1,1,5) do (
if !COPIED!==0 (
copy /Y "%SRC%" "%DST%" >nul 2>&1
if exist "%DST%" (
echo [%date% %time%] Copy attempt %%i done >> "%LOGFILE%"
set COPIED=1
) else (
echo [%date% %time%] Copy attempt %%i failed >> "%LOGFILE%"
timeout /t 2 /nobreak >nul
)
)
)
# Launch new exe via cmd start (clean environment, proper path handling) if %COPIED%==0 (
Start-Process cmd.exe -ArgumentList '/c', 'start', '""', ('"{0}"' -f $dst) -WindowStyle Hidden echo [%date% %time%] FAILED: could not copy after 5 attempts >> "%LOGFILE%"
pause
goto cleanup
)
# Cleanup echo [%date% %time%] Copy successful, launching... >> "%LOGFILE%"
Start-Sleep -Seconds 3 timeout /t 2 /nobreak >nul
Remove-Item -Path $src -Force -ErrorAction SilentlyContinue
Remove-Item -Path '{ps_path}' -Force -ErrorAction SilentlyContinue rem Launch new exe
echo [%date% %time%] Starting: %DST% >> "%LOGFILE%"
start "" "%DST%"
echo [%date% %time%] Launch command issued >> "%LOGFILE%"
timeout /t 2 /nobreak >nul
:cleanup
rem Delete downloaded update file
del /f /q "%SRC%" >nul 2>&1
echo [%date% %time%] Cleanup done >> "%LOGFILE%"
rem Self-delete
del /f /q "%~f0" >nul 2>&1
""" """
with open(ps_path, "w", encoding="utf-8") as f: # Enable delayed expansion for variables inside for loops
f.write(ps_content) bat_content = bat_content.replace("@echo off",
"@echo off\nsetlocal enabledelayedexpansion")
# VBS wrapper launches PowerShell hidden (no window flash) with open(bat_path, "w", encoding="utf-8") as f:
vbs_content = ( f.write(bat_content)
'CreateObject("Wscript.Shell").Run '
'"powershell -ExecutionPolicy Bypass -WindowStyle Hidden ' log.info(f"Update BAT: {bat_path}, log: {log_path}")
f'-File ""{ps_path}""""", 0, False\n'
# Launch BAT minimized (not hidden — need cmd.exe to run)
subprocess.Popen(
["cmd.exe", "/c", "start", "/min", "", bat_path],
creationflags=_SUBPROCESS_FLAGS,
) )
with open(vbs_path, "w") as f:
f.write(vbs_content)
os.startfile(vbs_path)
return True return True
except Exception as e: except Exception as e:

Binary file not shown.

View File

@@ -1,6 +1,6 @@
"""Version info for ServerManager.""" """Version info for ServerManager."""
__version__ = "1.8.92" __version__ = "1.8.93"
__app_name__ = "ServerManager" __app_name__ = "ServerManager"
__author__ = "aibot777" __author__ = "aibot777"
__description__ = "Desktop GUI for managing remote servers" __description__ = "Desktop GUI for managing remote servers"