summaryrefslogtreecommitdiffstats
path: root/internal/utils/version.go
blob: 950f6fa24bc4608f89ab1a7ce8e369f6605c4f0e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
package utils

import (
	"fmt"
	"os"
	"os/exec"
	"strings"
	"syscall"

	"github.com/spf13/cobra"
)

var Version = "dev"

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", "git.db.org.ai/dborg")
	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 {
	remote = strings.TrimPrefix(remote, "v")
	local = strings.TrimPrefix(local, "v")

	return remote != local && remote > local
}

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@latest' to update manually.\n\n")
		return
	}

	fmt.Fprintf(os.Stderr, "Updating to %s...\n", newVersion)

	installCmd := exec.Command("go", "install", "git.db.org.ai/dborg")
	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\n\n")
		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
}