Files
MyGoNavi/internal/app/update_install_mode.go
Syngnat 3ac390f0dc 🐛 fix(update): 支持 Windows 便携版 ZIP 自动更新
- 将便携版更新资产切换为普通 ZIP,并兼容复用已下载包
- 解压后映射版本化目标文件名,保留旧版 EXE 更新兼容
- 将内嵌 PowerShell 更新脚本策略收紧为 RemoteSigned
- 补充 ZIP、目标路径与启动参数回归测试
2026-07-23 13:07:03 +08:00

115 lines
3.4 KiB
Go

package app
import (
"errors"
"os"
"path/filepath"
"runtime"
"strings"
)
const windowsMSIInstallMarker = ".gonavi-msi-install"
type updateInstallMode string
const (
updateInstallModeUnknown updateInstallMode = "unknown"
updateInstallModePortable updateInstallMode = "portable"
updateInstallModeMSI updateInstallMode = "msi"
)
type updatePackageType string
const (
updatePackageTypePortable updatePackageType = "portable"
updatePackageTypeMSI updatePackageType = "msi"
updatePackageTypeDMG updatePackageType = "dmg"
updatePackageTypeArchive updatePackageType = "archive"
)
func resolveCurrentUpdateInstallMode() updateInstallMode {
return resolveUpdateInstallModeForExecutable(runtime.GOOS, updateResolveInstallTarget())
}
// IsWindowsMSIInstallExecutable reports whether executablePath belongs to a
// GoNavi MSI installation. The marker is packaged next to the stable MSI exe.
func IsWindowsMSIInstallExecutable(goos string, executablePath string) bool {
return resolveUpdateInstallModeForExecutable(goos, executablePath) == updateInstallModeMSI
}
func resolveUpdateInstallModeForExecutable(goos string, executablePath string) updateInstallMode {
if !strings.EqualFold(strings.TrimSpace(goos), "windows") {
return updateInstallModeUnknown
}
executablePath = strings.TrimSpace(executablePath)
if executablePath == "" {
return updateInstallModeUnknown
}
markerPath := filepath.Join(filepath.Dir(executablePath), windowsMSIInstallMarker)
info, err := os.Stat(markerPath)
if err == nil {
if info.IsDir() {
return updateInstallModeUnknown
}
return updateInstallModeMSI
}
if errors.Is(err, os.ErrNotExist) {
return updateInstallModePortable
}
return updateInstallModeUnknown
}
func resolveUpdatePackageType(goos string, installMode updateInstallMode) updatePackageType {
switch strings.ToLower(strings.TrimSpace(goos)) {
case "windows":
if installMode == updateInstallModeMSI {
return updatePackageTypeMSI
}
if installMode == updateInstallModePortable {
return updatePackageTypePortable
}
case "darwin":
return updatePackageTypeDMG
case "linux":
return updatePackageTypeArchive
}
return ""
}
func isUpdatePackageCompatibleWithInstallMode(goos string, installMode updateInstallMode, packageType updatePackageType, assetPath string) bool {
if !strings.EqualFold(strings.TrimSpace(goos), "windows") {
return true
}
extension := strings.ToLower(strings.TrimSpace(filepath.Ext(strings.TrimSpace(assetPath))))
switch installMode {
case updateInstallModeMSI:
return packageType == updatePackageTypeMSI && extension == ".msi"
case updateInstallModePortable:
return packageType == updatePackageTypePortable && (extension == ".zip" || extension == ".exe")
default:
return false
}
}
func validateUpdatePackageForCurrentInstallMode(goos string, declaredMode updateInstallMode, packageType updatePackageType, assetPath string) error {
if !strings.EqualFold(strings.TrimSpace(goos), "windows") {
return nil
}
currentMode := updateResolveInstallMode()
if currentMode == updateInstallModeUnknown || declaredMode != currentMode ||
!isUpdatePackageCompatibleWithInstallMode(goos, currentMode, packageType, assetPath) {
return localizedUpdateError{
key: "app.update.backend.error.online_update_unsupported",
params: map[string]any{
"platform": strings.Join([]string{
strings.TrimSpace(goos),
string(currentMode),
string(declaredMode),
string(packageType),
}, "/"),
},
}
}
return nil
}