From bec5013a4474e1213173c50e08292de7b869bf9f Mon Sep 17 00:00:00 2001 From: Syngnat Date: Thu, 26 Feb 2026 14:23:36 +0800 Subject: [PATCH] =?UTF-8?q?=F0=9F=90=9B=20fix(update-windows):=20=E4=BF=AE?= =?UTF-8?q?=E5=A4=8D=E8=87=AA=E5=8A=A8=E6=9B=B4=E6=96=B0=E8=84=9A=E6=9C=AC?= =?UTF-8?q?=E5=8F=98=E9=87=8F=E8=BD=AC=E4=B9=89=E5=AF=BC=E8=87=B4TARGET?= =?UTF-8?q?=E8=AF=AD=E6=B3=95=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 将 buildWindowsScript 改为模板占位符替换,避免 fmt.Sprintf 吞掉批处理百分号 - 修正 for %%I/%%F 语法,消除“此时不应有 TARGET~nxI”报错 - 保留原有更新重试与日志流程,不改变下载与安装主链路 - refs #112 --- internal/app/methods_update.go | 80 ++++++++++--------- .../app/methods_update_windows_script_test.go | 40 ++++++++++ 2 files changed, 84 insertions(+), 36 deletions(-) create mode 100644 internal/app/methods_update_windows_script_test.go diff --git a/internal/app/methods_update.go b/internal/app/methods_update.go index 3db56fa..20f1fdc 100644 --- a/internal/app/methods_update.go +++ b/internal/app/methods_update.go @@ -13,6 +13,7 @@ import ( "os/exec" "path/filepath" stdRuntime "runtime" + "strconv" "strings" "time" @@ -857,55 +858,55 @@ func launchLinuxUpdate(staged *stagedUpdate, targetExe string, pid int) error { } func buildWindowsScript(source, target, stagedDir, logPath string, pid int) string { - return fmt.Sprintf(`@echo off + script := `@echo off setlocal EnableExtensions EnableDelayedExpansion -set "SOURCE=%s" -set "TARGET=%s" -set "STAGED=%s" -set "LOG_FILE=%s" -set PID=%d +set "SOURCE=__GONAVI_UPDATE_SOURCE__" +set "TARGET=__GONAVI_UPDATE_TARGET__" +set "STAGED=__GONAVI_UPDATE_STAGED__" +set "LOG_FILE=__GONAVI_UPDATE_LOG__" +set PID=__GONAVI_UPDATE_PID__ call :log updater started -if not exist "%%SOURCE%%" ( - call :log source file not found: %%SOURCE%% +if not exist "%SOURCE%" ( + call :log source file not found: %SOURCE% exit /b 1 ) -for %%I in ("%%TARGET%%") do set "TARGET_NAME=%%~nxI" -for %%I in ("%%SOURCE%%") do set "SOURCE_EXT=%%~xI" +for %%I in ("%TARGET%") do set "TARGET_NAME=%%~nxI" +for %%I in ("%SOURCE%") do set "SOURCE_EXT=%%~xI" set "SOURCE_EXE=" -if /I "%%SOURCE_EXT%%"==".zip" ( - set "EXTRACT_DIR=%%STAGED%%\_extract" - if exist "%%EXTRACT_DIR%%" ( - rmdir /S /Q "%%EXTRACT_DIR%%" >> "%%LOG_FILE%%" 2>&1 +if /I "%SOURCE_EXT%"==".zip" ( + set "EXTRACT_DIR=%STAGED%\_extract" + if exist "%EXTRACT_DIR%" ( + rmdir /S /Q "%EXTRACT_DIR%" >> "%LOG_FILE%" 2>&1 ) - mkdir "%%EXTRACT_DIR%%" >> "%%LOG_FILE%%" 2>&1 - powershell -NoProfile -ExecutionPolicy Bypass -Command "$src=$env:SOURCE; $dst=$env:EXTRACT_DIR; Expand-Archive -LiteralPath $src -DestinationPath $dst -Force" >> "%%LOG_FILE%%" 2>&1 - if %%ERRORLEVEL%% NEQ 0 ( - call :log expand zip failed: %%SOURCE%% + mkdir "%EXTRACT_DIR%" >> "%LOG_FILE%" 2>&1 + powershell -NoProfile -ExecutionPolicy Bypass -Command "$src=$env:SOURCE; $dst=$env:EXTRACT_DIR; Expand-Archive -LiteralPath $src -DestinationPath $dst -Force" >> "%LOG_FILE%" 2>&1 + if %ERRORLEVEL% NEQ 0 ( + call :log expand zip failed: %SOURCE% exit /b 1 ) - if exist "%%EXTRACT_DIR%%\%%TARGET_NAME%%" ( - set "SOURCE_EXE=%%EXTRACT_DIR%%\%%TARGET_NAME%%" + if exist "%EXTRACT_DIR%\%TARGET_NAME%" ( + set "SOURCE_EXE=%EXTRACT_DIR%\%TARGET_NAME%" ) else ( - for /R "%%EXTRACT_DIR%%" %%F in (*.exe) do ( + for /R "%EXTRACT_DIR%" %%F in (*.exe) do ( if not defined SOURCE_EXE ( set "SOURCE_EXE=%%~fF" ) ) ) if not defined SOURCE_EXE ( - call :log no executable found in portable zip: %%SOURCE%% + call :log no executable found in portable zip: %SOURCE% exit /b 1 ) ) else ( - set "SOURCE_EXE=%%SOURCE%%" + set "SOURCE_EXE=%SOURCE%" ) :waitloop -tasklist /FI "PID eq %%PID%%" | find "%%PID%%" >nul -if %%ERRORLEVEL%%==0 ( +tasklist /FI "PID eq %PID%" | find "%PID%" >nul +if %ERRORLEVEL%==0 ( timeout /t 1 /nobreak >nul goto waitloop ) @@ -913,11 +914,11 @@ call :log host process exited set /a RETRY=0 :move_retry -move /Y "%%SOURCE_EXE%%" "%%TARGET%%" >> "%%LOG_FILE%%" 2>&1 -if %%ERRORLEVEL%%==0 goto move_done +move /Y "%SOURCE_EXE%" "%TARGET%" >> "%LOG_FILE%" 2>&1 +if %ERRORLEVEL%==0 goto move_done -copy /Y "%%SOURCE_EXE%%" "%%TARGET%%" >> "%%LOG_FILE%%" 2>&1 -if %%ERRORLEVEL%%==0 goto move_done +copy /Y "%SOURCE_EXE%" "%TARGET%" >> "%LOG_FILE%" 2>&1 +if %ERRORLEVEL%==0 goto move_done set /a RETRY+=1 if !RETRY! LSS 20 ( @@ -929,23 +930,30 @@ call :log replace failed after retries (portable mode, no elevation): check dire exit /b 1 :move_done -start "" "%%TARGET%%" >> "%%LOG_FILE%%" 2>&1 -if %%ERRORLEVEL%% NEQ 0 ( +start "" "%TARGET%" >> "%LOG_FILE%" 2>&1 +if %ERRORLEVEL% NEQ 0 ( call :log cmd start failed, trying powershell Start-Process - powershell -NoProfile -ExecutionPolicy Bypass -Command "Start-Process -FilePath '%%TARGET%%'" >> "%%LOG_FILE%%" 2>&1 - if %%ERRORLEVEL%% NEQ 0 ( + powershell -NoProfile -ExecutionPolicy Bypass -Command "Start-Process -FilePath '%TARGET%'" >> "%LOG_FILE%" 2>&1 + if %ERRORLEVEL% NEQ 0 ( call :log relaunch failed exit /b 1 ) ) -rmdir /S /Q "%%STAGED%%" >> "%%LOG_FILE%%" 2>&1 +rmdir /S /Q "%STAGED%" >> "%LOG_FILE%" 2>&1 call :log update finished exit /b 0 :log -echo [%%date%% %%time%%] %%*>>"%%LOG_FILE%%" +echo [%date% %time%] %*>>"%LOG_FILE%" exit /b 0 -`, source, target, stagedDir, logPath, pid) +` + return strings.NewReplacer( + "__GONAVI_UPDATE_SOURCE__", source, + "__GONAVI_UPDATE_TARGET__", target, + "__GONAVI_UPDATE_STAGED__", stagedDir, + "__GONAVI_UPDATE_LOG__", logPath, + "__GONAVI_UPDATE_PID__", strconv.Itoa(pid), + ).Replace(script) } func buildMacScript(dmgPath, targetApp, stagedDir, mountDir, logPath string, pid int) string { diff --git a/internal/app/methods_update_windows_script_test.go b/internal/app/methods_update_windows_script_test.go new file mode 100644 index 0000000..9313497 --- /dev/null +++ b/internal/app/methods_update_windows_script_test.go @@ -0,0 +1,40 @@ +package app + +import ( + "strings" + "testing" +) + +func TestBuildWindowsScriptKeepsBatchForSyntax(t *testing.T) { + script := buildWindowsScript( + `C:\tmp\GoNavi-v0.4.0-windows-amd64.zip`, + `C:\Program Files\GoNavi\GoNavi.exe`, + `C:\Program Files\GoNavi\.gonavi-update-windows-v0.4.0`, + `C:\Program Files\GoNavi\logs\update-install.log`, + 13579, + ) + + mustContain := []string{ + `for %%I in ("%TARGET%") do set "TARGET_NAME=%%~nxI"`, + `for %%I in ("%SOURCE%") do set "SOURCE_EXT=%%~xI"`, + `for /R "%EXTRACT_DIR%" %%F in (*.exe) do (`, + `set "SOURCE_EXE=%%~fF"`, + } + for _, want := range mustContain { + if !strings.Contains(script, want) { + t.Fatalf("windows update script missing required token: %s\nscript:\n%s", want, script) + } + } + + mustNotContain := []string{ + `for %I in ("%TARGET%") do set "TARGET_NAME=%~nxI"`, + `for %I in ("%SOURCE%") do set "SOURCE_EXT=%~xI"`, + `for /R "%EXTRACT_DIR%" %F in (*.exe) do (`, + `set "SOURCE_EXE=%~fF"`, + } + for _, bad := range mustNotContain { + if strings.Contains(script, bad) { + t.Fatalf("windows update script contains invalid batch syntax: %s\nscript:\n%s", bad, script) + } + } +}