package main import ( "crypto/sha256" "fmt" "io" "net/http" "os" "os/exec" "path/filepath" "sentinel/pkg/log" "sentinel/pkg/utils" "sentinel/pkg/device" "sentinel/pkg/net" ) func main() { deviceID := device.GetDeviceID() fmt.Printf("[updater] device id: %s\n", deviceID) exeDir, _ := os.Executable() target := filepath.Join(filepath.Dir(exeDir), "main_program_binary_name") // TODO: 替换 if err := RunUpdate(deviceID, target); err != nil { log.Fatalf("[updater] update failed: %v", err) } fmt.Println("[updater] update finished") } // RunUpdate 检查更新、下载、替换主程序并启动新程序 func RunUpdate(deviceID string, targetExe string) error { info, err := api.CheckUpdate(deviceID) if err != nil { return err } // 2. 比对本地版本 if info.Version <= utils.APP_VERSION { fmt.Println("[updater] already latest version:", utils.APP_VERSION) return nil } fmt.Println("[updater] updating to version:", info.Version, "notes:", info.Notes) // 3. 下载新版本到临时目录 tmpFile := filepath.Join(os.TempDir(), "new_program_tmp") out, err := os.Create(tmpFile) if err != nil { return fmt.Errorf("create temp file failed: %w", err) } defer out.Close() resp2, err := http.Get(info.DownloadURL) if err != nil { return fmt.Errorf("download failed: %w", err) } defer resp2.Body.Close() h := sha256.New() mw := io.MultiWriter(out, h) if _, err := io.Copy(mw, resp2.Body); err != nil { return fmt.Errorf("write temp file failed: %w", err) } // 4. 替换 targetExe backup := targetExe + ".bak" _ = os.Remove(backup) _ = os.Rename(targetExe, backup) // 备份旧版本 if err := os.Rename(tmpFile, targetExe); err != nil { return fmt.Errorf("replace main program failed: %w", err) } fmt.Println("[updater] replaced main program") // 5. 启动新主程序 cmd := exec.Command(targetExe) cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr if err := cmd.Start(); err != nil { return fmt.Errorf("start new program failed: %w", err) } fmt.Println("[updater] new program started successfully") return nil }