package utils import ( "fmt" "os" "os/exec" "runtime/debug" "strings" "syscall" "github.com/spf13/cobra" ) var ( Version = "dev" RepositoryURL = "git@45.38.20.139:repos/dborg.git" ) func init() { if Version == "dev" { if info, ok := debug.ReadBuildInfo(); ok { if info.Main.Version != "" && info.Main.Version != "(devel)" { Version = info.Main.Version } } } } func CheckForUpdates(cmd *cobra.Command) error { if !isTerminal() { return nil } latestVersion, err := getLatestRemoteTag() if err != nil { return nil } if latestVersion == "" || latestVersion == Version { return nil } if isNewerVersion(latestVersion, Version) { promptAndUpdate(latestVersion) } return nil } func getLatestRemoteTag() (string, error) { cmd := exec.Command("git", "ls-remote", "--tags", "--refs", "--sort=-v:refname", RepositoryURL) output, err := cmd.Output() if err != nil { return "", err } lines := strings.Split(strings.TrimSpace(string(output)), "\n") if len(lines) == 0 { return "", fmt.Errorf("no tags found") } parts := strings.Split(lines[0], "refs/tags/") if len(parts) < 2 { return "", fmt.Errorf("invalid tag format") } return strings.TrimSpace(parts[1]), nil } func isNewerVersion(remote, local string) bool { if local == "dev" || local == "(devel)" { return false } remote = strings.TrimPrefix(remote, "v") local = strings.TrimPrefix(local, "v") if remote == local { return false } remoteParts := strings.Split(remote, ".") localParts := strings.Split(local, ".") for i := 0; i < len(remoteParts) && i < len(localParts); i++ { if remoteParts[i] > localParts[i] { return true } if remoteParts[i] < localParts[i] { return false } } return len(remoteParts) > len(localParts) } func promptAndUpdate(newVersion string) { fmt.Fprintf(os.Stderr, "\nšŸ”” A new version of dborg is available: %s (current: %s)\n", newVersion, Version) fmt.Fprintf(os.Stderr, "Would you like to update now? [Y/n]: ") var response string fmt.Scanln(&response) response = strings.ToLower(strings.TrimSpace(response)) if response != "" && response != "y" && response != "yes" { fmt.Fprintf(os.Stderr, "Update skipped. Run 'go install git.db.org.ai/dborg@%s' to update manually.\n\n", newVersion) return } fmt.Fprintf(os.Stderr, "Updating to %s...\n", newVersion) installTarget := fmt.Sprintf("git.db.org.ai/dborg@%s", newVersion) installCmd := exec.Command("go", "install", installTarget) installCmd.Stdout = os.Stderr installCmd.Stderr = os.Stderr if err := installCmd.Run(); err != nil { fmt.Fprintf(os.Stderr, "Failed to update: %v\n", err) fmt.Fprintf(os.Stderr, "Please update manually: go install git.db.org.ai/dborg@%s\n\n", newVersion) return } fmt.Fprintf(os.Stderr, "āœ“ Update successful! Restarting...\n\n") restartSelf() } func restartSelf() { executable, err := os.Executable() if err != nil { fmt.Fprintf(os.Stderr, "Failed to get executable path: %v\n", err) os.Exit(1) } args := os.Args[1:] err = syscall.Exec(executable, append([]string{executable}, args...), os.Environ()) if err != nil { fmt.Fprintf(os.Stderr, "Failed to restart: %v\n", err) os.Exit(1) } } func isTerminal() bool { fileInfo, err := os.Stdout.Stat() if err != nil { return false } return (fileInfo.Mode() & os.ModeCharDevice) != 0 }