1package common
2
3import (
4 "encoding/json"
5 "fmt"
6 "io"
7 "net/http"
8 "runtime"
9 "time"
10
11 "github.com/bnema/gordon/pkg/docker"
12 "github.com/charmbracelet/log"
13)
14
15type CurrentVersion struct {
16 isContainer bool
17 Version string `json:"version"`
18}
19
20type ArchVersion struct {
21 Name string `json:"name"`
22}
23
24type VersionInfo struct {
25 Name string `json:"name"`
26}
27
28type VersionsResponse struct {
29 AMD64 string `json:"amd64"`
30 ARM64 string `json:"arm64"`
31}
32
33func GetCurrentBuildVersionInfo(c *Config) CurrentVersion {
34 return CurrentVersion{
35 isContainer: docker.IsRunningInContainer(),
36 Version: c.GetVersion(),
37 }
38}
39
40func CheckForNewVersion(localVersion, remoteVersion string) string {
41 if localVersion != remoteVersion {
42 return "A new version is available"
43 }
44 return ""
45}
46
47func getArch() string {
48 return runtime.GOARCH
49}
50
51func checkAndUpdateVersion(c *Config) (string, error) {
52 localVersion := GetCurrentBuildVersionInfo(c)
53 remoteVersions, err := getRemoteVersion(c)
54 if err != nil {
55 return "", err
56 }
57
58 var remoteVersion string
59 switch getArch() {
60 case "amd64":
61 remoteVersion = remoteVersions.AMD64
62 case "arm64":
63 remoteVersion = remoteVersions.ARM64
64 default:
65 return "", fmt.Errorf("unsupported architecture: %s", getArch())
66 }
67
68 // Check if remoteVersion is empty
69 if remoteVersion == "" {
70 return "", fmt.Errorf("remote version not available for architecture %s", getArch())
71 }
72
73 message := CheckForNewVersion(localVersion.Version, remoteVersion)
74
75 // Return the message without an error, even if it's empty
76 return message, nil
77}
78
79// GetVersion returns the version
80func (c *Config) GetVersion() string {
81 if c.Build.BuildVersion != "" {
82 return c.Build.BuildVersion
83 }
84 return "devel"
85}
86
87func CheckVersionPeriodically(c *Config) (string, error) {
88 // Check if version is "devel"
89 if c.GetVersion() == "devel" {
90 return "", nil
91 }
92
93 // Perform an immediate check.
94 newVersionMessage, err := checkAndUpdateVersion(c)
95
96 if err != nil {
97 return "", err
98 }
99
100 if newVersionMessage != "" {
101 log.Info(newVersionMessage)
102 return "", nil
103 }
104
105 // Set up a ticker to check every 5 minutes.
106 timer := time.NewTicker(5 * time.Minute)
107 defer timer.Stop()
108
109 for range timer.C {
110 newVersionMessage, err = checkAndUpdateVersion(c)
111 if err != nil {
112 log.Printf("Error fetching remote version: %s", err)
113 continue
114 }
115
116 if newVersionMessage != "" {
117 fmt.Println(newVersionMessage)
118 return "", nil //We return so the loop stops in case of a new version
119 }
120 }
121
122 return "", nil
123}
124
125func getRemoteVersion(c *Config) (VersionsResponse, error) {
126 proxyURL := c.Build.ProxyURL
127 if proxyURL == "" {
128 return VersionsResponse{}, fmt.Errorf("proxy URL is empty")
129 }
130
131 checkVersionEndpoint := proxyURL + "/version"
132 resp, err := http.Get(checkVersionEndpoint)
133 if err != nil {
134 return VersionsResponse{}, err
135 }
136
137 defer resp.Body.Close()
138
139 body, err := io.ReadAll(resp.Body)
140 if err != nil {
141 return VersionsResponse{}, err
142 }
143
144 var remoteVersionInfo VersionsResponse
145 err = json.Unmarshal(body, &remoteVersionInfo)
146 if err != nil {
147 return VersionsResponse{}, err
148 }
149
150 return remoteVersionInfo, nil
151}