mirror of
https://github.com/Syngnat/GoNavi.git
synced 2026-05-07 04:42:51 +08:00
@@ -23,7 +23,8 @@
|
||||
| #318 | mysql,bit 列,修改成 1 失败 | Fixed | `bee78be` |
|
||||
| #319 | 关于运行外部 sql 文件的一些建议 | Deferred | - |
|
||||
| #320 | 无法连接达梦数据库 | Investigating | - |
|
||||
| #327 | SHOW DATABASES 报错 | Fixed | Pending |
|
||||
| #327 | SHOW DATABASES 报错 | Fixed | `5ac0221` |
|
||||
| #328 | [Bug] 安装更新失败 | Fixed | Pending |
|
||||
|
||||
## Notes
|
||||
|
||||
@@ -55,6 +56,12 @@
|
||||
- 处理:为数据库列表查询增加 `SELECT DATABASE()` 回退,仅保留当前连接库时也能正常展示。
|
||||
- 验证:补充 `internal/db/mysql_metadata_test.go` 回归测试,覆盖有权限、多库和低权限回退场景。
|
||||
|
||||
### #328
|
||||
|
||||
- 根因:Windows 更新脚本在批处理执行、错误码读取和重启命令上不够稳,`cmd /C start`、LF 行尾和块内 `%ERRORLEVEL%` 在实际环境下容易引发安装失败。
|
||||
- 处理:更新脚本统一输出为 CRLF,块内错误码改为延迟展开,旧文件回退路径统一为 `TARGET_OLD`,并将脚本启动方式收敛为 `cmd.exe /D /C call <script>`。
|
||||
- 验证:补充 `internal/app/methods_update_windows_script_test.go`,覆盖批处理语法、Win10 回退路径、CRLF 行尾、延迟展开和启动命令构造。
|
||||
|
||||
## Next
|
||||
|
||||
- 继续处理下一个最早且可直接落地的开放 issue。
|
||||
|
||||
@@ -864,7 +864,7 @@ func launchWindowsUpdate(staged *stagedUpdate, targetExe string, pid int) error
|
||||
}
|
||||
|
||||
logger.Infof("启动 Windows 更新脚本:target=%s script=%s log=%s", targetExe, scriptPath, logPath)
|
||||
cmd := exec.Command("cmd", "/C", "start", "", scriptPath)
|
||||
cmd := buildWindowsLaunchCommand(scriptPath)
|
||||
return cmd.Start()
|
||||
}
|
||||
|
||||
@@ -907,6 +907,7 @@ func buildWindowsScript(source, target, stagedDir, logPath string, pid int) stri
|
||||
setlocal EnableExtensions EnableDelayedExpansion
|
||||
set "SOURCE=__GONAVI_UPDATE_SOURCE__"
|
||||
set "TARGET=__GONAVI_UPDATE_TARGET__"
|
||||
set "TARGET_OLD=%TARGET%.old"
|
||||
set "STAGED=__GONAVI_UPDATE_STAGED__"
|
||||
set "LOG_FILE=__GONAVI_UPDATE_LOG__"
|
||||
set PID=__GONAVI_UPDATE_PID__
|
||||
@@ -928,7 +929,7 @@ if /I "%SOURCE_EXT%"==".zip" (
|
||||
)
|
||||
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 (
|
||||
if !ERRORLEVEL! NEQ 0 (
|
||||
call :log expand zip failed: %SOURCE%
|
||||
exit /b 1
|
||||
)
|
||||
@@ -964,15 +965,15 @@ call :log cooldown finished, starting file replace
|
||||
set /a RETRY=0
|
||||
:move_retry
|
||||
call :log attempt !RETRY!: trying rename-then-copy strategy
|
||||
ren "%TARGET%" "%TARGET_NAME%.old" >> "%LOG_FILE%" 2>&1
|
||||
if %ERRORLEVEL%==0 (
|
||||
move /Y "%TARGET%" "%TARGET_OLD%" >> "%LOG_FILE%" 2>&1
|
||||
if !ERRORLEVEL!==0 (
|
||||
copy /Y "%SOURCE_EXE%" "%TARGET%" >> "%LOG_FILE%" 2>&1
|
||||
if !ERRORLEVEL!==0 (
|
||||
del /F /Q "%TARGET%.old" >> "%LOG_FILE%" 2>&1
|
||||
del /F /Q "%TARGET_OLD%" >> "%LOG_FILE%" 2>&1
|
||||
goto move_done
|
||||
)
|
||||
call :log copy after rename failed, restoring old file
|
||||
ren "%TARGET_NAME%.old" "%TARGET_NAME%" >> "%LOG_FILE%" 2>&1
|
||||
move /Y "%TARGET_OLD%" "%TARGET%" >> "%LOG_FILE%" 2>&1
|
||||
)
|
||||
|
||||
call :log rename strategy failed, trying direct move
|
||||
@@ -997,12 +998,12 @@ call :log replace failed after retries (portable mode, no elevation): check dire
|
||||
exit /b 1
|
||||
|
||||
:move_done
|
||||
del /F /Q "%TARGET%.old" >> "%LOG_FILE%" 2>&1
|
||||
del /F /Q "%TARGET_OLD%" >> "%LOG_FILE%" 2>&1
|
||||
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 (
|
||||
if !ERRORLEVEL! NEQ 0 (
|
||||
call :log relaunch failed
|
||||
exit /b 1
|
||||
)
|
||||
@@ -1021,7 +1022,11 @@ exit /b 0
|
||||
"__GONAVI_UPDATE_STAGED__", stagedDir,
|
||||
"__GONAVI_UPDATE_LOG__", logPath,
|
||||
"__GONAVI_UPDATE_PID__", strconv.Itoa(pid),
|
||||
).Replace(script)
|
||||
).Replace(strings.ReplaceAll(script, "\n", "\r\n"))
|
||||
}
|
||||
|
||||
func buildWindowsLaunchCommand(scriptPath string) *exec.Cmd {
|
||||
return exec.Command("cmd.exe", "/D", "/C", "call", scriptPath)
|
||||
}
|
||||
|
||||
func buildMacScript(dmgPath, targetApp, stagedDir, mountDir, logPath string, pid int) string {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"os/exec"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
@@ -55,15 +56,15 @@ func TestBuildWindowsScriptWin10Fixes(t *testing.T) {
|
||||
}{
|
||||
{"cooldown after process exit", `timeout /t 3 /nobreak >nul`},
|
||||
{"cooldown log", `call :log cooldown finished, starting file replace`},
|
||||
{"rename-before-replace strategy", `ren "%TARGET%" "%TARGET_NAME%.old"`},
|
||||
{"rename-before-replace strategy", `move /Y "%TARGET%" "%TARGET_OLD%"`},
|
||||
{"copy after rename", `copy /Y "%SOURCE_EXE%" "%TARGET%"`},
|
||||
{"restore on copy failure", `ren "%TARGET_NAME%.old" "%TARGET_NAME%"`},
|
||||
{"restore on copy failure", `move /Y "%TARGET_OLD%" "%TARGET%"`},
|
||||
{"direct move fallback", `call :log rename strategy failed, trying direct move`},
|
||||
{"exponential backoff tier 1", `if !RETRY! GEQ 3 set /a WAIT=2`},
|
||||
{"exponential backoff tier 2", `if !RETRY! GEQ 6 set /a WAIT=3`},
|
||||
{"exponential backoff tier 3", `if !RETRY! GEQ 9 set /a WAIT=5`},
|
||||
{"retry limit 15", `if !RETRY! LSS 15`},
|
||||
{"cleanup old file", `del /F /Q "%TARGET%.old"`},
|
||||
{"cleanup old file", `del /F /Q "%TARGET_OLD%"`},
|
||||
}
|
||||
for _, fix := range win10Fixes {
|
||||
if !strings.Contains(script, fix.token) {
|
||||
@@ -72,3 +73,59 @@ func TestBuildWindowsScriptWin10Fixes(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuildWindowsScriptUsesCRLFLineEndings(t *testing.T) {
|
||||
script := buildWindowsScript(
|
||||
`C:\tmp\GoNavi-v0.5.0-windows-amd64.exe`,
|
||||
`C:\Program Files\GoNavi\GoNavi.exe`,
|
||||
`C:\Program Files\GoNavi\.gonavi-update-windows-v0.5.0`,
|
||||
`C:\Program Files\GoNavi\logs\update-install.log`,
|
||||
99999,
|
||||
)
|
||||
|
||||
if !strings.Contains(script, "\r\n") {
|
||||
t.Fatalf("windows update script should use CRLF line endings")
|
||||
}
|
||||
if strings.Contains(script, "@echo off\nsetlocal") {
|
||||
t.Fatalf("windows update script should not contain LF-only line endings")
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuildWindowsScriptUsesDelayedErrorlevelInsideBlocks(t *testing.T) {
|
||||
script := buildWindowsScript(
|
||||
`C:\tmp\GoNavi-v0.5.0-windows-amd64.zip`,
|
||||
`C:\Program Files\GoNavi\GoNavi.exe`,
|
||||
`C:\Program Files\GoNavi\.gonavi-update-windows-v0.5.0`,
|
||||
`C:\Program Files\GoNavi\logs\update-install.log`,
|
||||
99999,
|
||||
)
|
||||
|
||||
for _, token := range []string{
|
||||
`if !ERRORLEVEL! NEQ 0 (`,
|
||||
`powershell -NoProfile -ExecutionPolicy Bypass -Command "Start-Process -FilePath '%TARGET%'" >> "%LOG_FILE%" 2>&1`,
|
||||
`set "TARGET_OLD=%TARGET%.old"`,
|
||||
} {
|
||||
if !strings.Contains(script, token) {
|
||||
t.Fatalf("windows update script missing token: %s\nscript:\n%s", token, script)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuildWindowsLaunchCommandUsesDirectCall(t *testing.T) {
|
||||
cmd := buildWindowsLaunchCommand(`C:\tmp\gonavi-update\update.cmd`)
|
||||
|
||||
if !strings.EqualFold(cmd.Args[0], cmd.Path) && !strings.HasSuffix(strings.ToLower(cmd.Path), `\cmd.exe`) {
|
||||
t.Fatalf("unexpected command path: %s", cmd.Path)
|
||||
}
|
||||
|
||||
want := []string{"cmd.exe", "/D", "/C", "call", `C:\tmp\gonavi-update\update.cmd`}
|
||||
if len(cmd.Args) != len(want) {
|
||||
t.Fatalf("unexpected arg length: got %d want %d, args=%v", len(cmd.Args), len(want), cmd.Args)
|
||||
}
|
||||
for i := range want {
|
||||
if cmd.Args[i] != want[i] {
|
||||
t.Fatalf("unexpected arg[%d]: got %q want %q", i, cmd.Args[i], want[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var _ = exec.ErrNotFound
|
||||
|
||||
Reference in New Issue
Block a user