feat: add checksum-based update detection
This commit is contained in:
@@ -2,6 +2,8 @@ package updater
|
||||
|
||||
import (
|
||||
"archive/zip"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
@@ -43,6 +45,7 @@ type UpdateInfo struct {
|
||||
ReleaseNotes string `json:"releaseNotes"`
|
||||
DownloadURL string `json:"downloadURL"`
|
||||
DownloadSize int64 `json:"downloadSize"`
|
||||
IsRebuild bool `json:"isRebuild"`
|
||||
}
|
||||
|
||||
type Updater struct {
|
||||
@@ -89,16 +92,40 @@ func (u *Updater) CheckForUpdates() (*UpdateInfo, error) {
|
||||
|
||||
u.downloadURL = downloadAsset.BrowserDownloadURL
|
||||
|
||||
// Find checksum asset
|
||||
var checksumAsset *Asset
|
||||
for i := range release.Assets {
|
||||
if strings.Contains(release.Assets[i].Name, "macos-arm64") && strings.HasSuffix(release.Assets[i].Name, ".sha256") {
|
||||
checksumAsset = &release.Assets[i]
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
latestVersion := strings.TrimPrefix(release.TagName, "v")
|
||||
currentVersion := strings.TrimPrefix(version.Version, "v")
|
||||
|
||||
isNewer := isNewerVersion(latestVersion, currentVersion)
|
||||
isRebuild := false
|
||||
|
||||
// Check if same version but different checksum (rebuild)
|
||||
if !isNewer && checksumAsset != nil {
|
||||
remoteChecksum, err := u.downloadChecksum(checksumAsset.BrowserDownloadURL)
|
||||
if err == nil {
|
||||
localChecksum, err := u.calculateBinaryChecksum()
|
||||
if err == nil && remoteChecksum != localChecksum {
|
||||
isRebuild = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
info := &UpdateInfo{
|
||||
Available: isNewerVersion(latestVersion, currentVersion),
|
||||
Available: isNewer || isRebuild,
|
||||
CurrentVersion: version.Version,
|
||||
LatestVersion: release.TagName,
|
||||
ReleaseNotes: release.Body,
|
||||
DownloadURL: downloadAsset.BrowserDownloadURL,
|
||||
DownloadSize: downloadAsset.Size,
|
||||
IsRebuild: isRebuild,
|
||||
}
|
||||
|
||||
return info, nil
|
||||
@@ -304,6 +331,54 @@ func (u *Updater) copyDir(src, dst string) error {
|
||||
})
|
||||
}
|
||||
|
||||
// downloadChecksum fetches the remote SHA256 checksum file
|
||||
func (u *Updater) downloadChecksum(url string) (string, error) {
|
||||
client := &http.Client{Timeout: 10 * time.Second}
|
||||
resp, err := client.Get(url)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer func() { _ = resp.Body.Close() }()
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return "", fmt.Errorf("failed to download checksum: status %d", resp.StatusCode)
|
||||
}
|
||||
|
||||
data, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return strings.TrimSpace(string(data)), nil
|
||||
}
|
||||
|
||||
// calculateBinaryChecksum calculates SHA256 of the current running binary
|
||||
func (u *Updater) calculateBinaryChecksum() (string, error) {
|
||||
execPath, err := os.Executable()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// Resolve symlinks to get actual binary path
|
||||
execPath, err = filepath.EvalSymlinks(execPath)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
file, err := os.Open(execPath)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer func() { _ = file.Close() }()
|
||||
|
||||
hasher := sha256.New()
|
||||
if _, err := io.Copy(hasher, file); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return hex.EncodeToString(hasher.Sum(nil)), nil
|
||||
}
|
||||
|
||||
// isNewerVersion compares semver-like versions (e.g., "0.1.0" vs "0.2.0")
|
||||
func isNewerVersion(latest, current string) bool {
|
||||
if current == "dev" || current == "unknown" {
|
||||
|
||||
Reference in New Issue
Block a user