refactor: use version infos via ldflags and simplify version checking logic
- commit
- 4bf9f062f38862e7ae94bb4606f49f9978602548
- author
- Brice · · 2024-11-20 09:09 UTC
- parent
- 8b73233
- signature
- unsigned
8 files · +175 -120
M Makefile +15 -3
| @@ -5,6 +5,17 @@ TAG := dev | |||
| 5 | 5 | DIST_DIR := ./dist | |
| 6 | 6 | ENGINE := podman | |
| 7 | 7 | ||
| 8 | + | # Version information | |
| 9 | + | VERSION := $(shell git describe --tags --always --dirty) | |
| 10 | + | COMMIT := $(shell git rev-parse --short HEAD) | |
| 11 | + | BUILD_DATE := $(shell date -u '+%Y-%m-%d_%I:%M:%S%p') | |
| 12 | + | ||
| 13 | + | # Build flags | |
| 14 | + | LDFLAGS := -s -w \ | |
| 15 | + | -X main.version=$(VERSION) \ | |
| 16 | + | -X main.commit=$(COMMIT) \ | |
| 17 | + | -X main.date=$(BUILD_DATE) | |
| 18 | + | ||
| 8 | 19 | # Architectures | |
| 9 | 20 | ARCHS := amd64 arm64 | |
| 10 | 21 | ||
| @@ -19,8 +30,9 @@ build: | |||
| 19 | 30 | @echo "Building Go binaries..." | |
| 20 | 31 | @mkdir -p $(DIST_DIR) | |
| 21 | 32 | @rm -f $(DIST_DIR)/* | |
| 22 | - | @CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o $(DIST_DIR)/gordon-linux-amd64 ./main.go | |
| 23 | - | @CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -o $(DIST_DIR)/gordon-linux-arm64 ./main.go | |
| 33 | + | @echo "Building with version $(VERSION), commit $(COMMIT), date $(BUILD_DATE)" | |
| 34 | + | @CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags="$(LDFLAGS)" -o $(DIST_DIR)/gordon-linux-amd64 ./main.go | |
| 35 | + | @CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -ldflags="$(LDFLAGS)" -o $(DIST_DIR)/gordon-linux-arm64 ./main.go | |
| 24 | 36 | @echo "Go binaries built successfully" | |
| 25 | 37 | ||
| 26 | 38 | # Build and push Docker images | |
| @@ -61,4 +73,4 @@ clean: | |||
| 61 | 73 | @echo "Cleaning up..." | |
| 62 | 74 | @rm -rf $(DIST_DIR) | |
| 63 | 75 | @$(ENGINE) system prune -f | |
| 64 | - | @echo "Cleanup completed."⏎̸ | |
| 76 | + | @echo "Cleanup completed." | |
| @@ -5,6 +5,17 @@ TAG := dev | |||||
| 5 | DIST_DIR := ./dist | 5 | DIST_DIR := ./dist | ||
| 6 | ENGINE := podman | 6 | ENGINE := podman | ||
| 7 | 7 | ||||
| 8 | + | # Version information | |||
| 9 | + | VERSION := $(shell git describe --tags --always --dirty) | |||
| 10 | + | COMMIT := $(shell git rev-parse --short HEAD) | |||
| 11 | + | BUILD_DATE := $(shell date -u '+%Y-%m-%d_%I:%M:%S%p') | |||
| 12 | + | ||||
| 13 | + | # Build flags | |||
| 14 | + | LDFLAGS := -s -w \ | |||
| 15 | + | -X main.version=$(VERSION) \ | |||
| 16 | + | -X main.commit=$(COMMIT) \ | |||
| 17 | + | -X main.date=$(BUILD_DATE) | |||
| 18 | + | ||||
| 8 | # Architectures | 19 | # Architectures | ||
| 9 | ARCHS := amd64 arm64 | 20 | ARCHS := amd64 arm64 | ||
| 10 | 21 | ||||
| @@ -19,8 +30,9 @@ build: | |||||
| 19 | @echo "Building Go binaries..." | 30 | @echo "Building Go binaries..." | ||
| 20 | @mkdir -p $(DIST_DIR) | 31 | @mkdir -p $(DIST_DIR) | ||
| 21 | @rm -f $(DIST_DIR)/* | 32 | @rm -f $(DIST_DIR)/* | ||
| 22 | - | @CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o $(DIST_DIR)/gordon-linux-amd64 ./main.go | 33 | + | @echo "Building with version $(VERSION), commit $(COMMIT), date $(BUILD_DATE)" |
| 23 | - | @CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -o $(DIST_DIR)/gordon-linux-arm64 ./main.go | 34 | + | @CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags="$(LDFLAGS)" -o $(DIST_DIR)/gordon-linux-amd64 ./main.go |
| 35 | + | @CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -ldflags="$(LDFLAGS)" -o $(DIST_DIR)/gordon-linux-arm64 ./main.go | |||
| 24 | @echo "Go binaries built successfully" | 36 | @echo "Go binaries built successfully" | ||
| 25 | 37 | ||||
| 26 | # Build and push Docker images | 38 | # Build and push Docker images | ||
| @@ -61,4 +73,4 @@ clean: | |||||
| 61 | @echo "Cleaning up..." | 73 | @echo "Cleaning up..." | ||
| 62 | @rm -rf $(DIST_DIR) | 74 | @rm -rf $(DIST_DIR) | ||
| 63 | @$(ENGINE) system prune -f | 75 | @$(ENGINE) system prune -f | ||
| 64 | - | @echo "Cleanup completed."⏎̸ | 76 | + | @echo "Cleanup completed." |
M cmd/gordon.go +4 -16
| @@ -2,13 +2,12 @@ package cmd | |||
| 2 | 2 | ||
| 3 | 3 | import ( | |
| 4 | 4 | "fmt" | |
| 5 | - | "regexp" | |
| 5 | + | "time" | |
| 6 | 6 | ||
| 7 | 7 | "github.com/bnema/gordon/internal/cli" | |
| 8 | 8 | "github.com/bnema/gordon/internal/cli/cmd" | |
| 9 | 9 | "github.com/bnema/gordon/internal/common" | |
| 10 | 10 | "github.com/bnema/gordon/internal/server" | |
| 11 | - | "github.com/charmbracelet/log" | |
| 12 | 11 | "github.com/spf13/cobra" | |
| 13 | 12 | ||
| 14 | 13 | // env auto load | |
| @@ -16,9 +15,6 @@ import ( | |||
| 16 | 15 | ) | |
| 17 | 16 | ||
| 18 | 17 | var ( | |
| 19 | - | build string | |
| 20 | - | commit string | |
| 21 | - | date string | |
| 22 | 18 | rootCmd = &cobra.Command{Use: "gordon"} | |
| 23 | 19 | proxyURL = "https://gordon-proxy.bamen.dev" | |
| 24 | 20 | ) | |
| @@ -38,8 +34,7 @@ func Execute(client *cli.App, server *server.App) { | |||
| 38 | 34 | cobra.CheckErr(rootCmd.Execute()) | |
| 39 | 35 | } | |
| 40 | 36 | ||
| 41 | - | func ExecuteCLI() { | |
| 42 | - | build = regexp.MustCompile(`\d+\.\d+\.\d+`).FindString(build) | |
| 37 | + | func ExecuteCLI(build, commit, date string) { | |
| 43 | 38 | buildInfo := &common.BuildConfig{ | |
| 44 | 39 | BuildVersion: build, | |
| 45 | 40 | BuildCommit: commit, | |
| @@ -58,15 +53,8 @@ func ExecuteCLI() { | |||
| 58 | 53 | ||
| 59 | 54 | common.DockerInit(&s.Config.ContainerEngine) | |
| 60 | 55 | ||
| 61 | - | // Check for new version | |
| 62 | - | go func() { | |
| 63 | - | msg, err := common.CheckVersionPeriodically(&s.Config) | |
| 64 | - | if err != nil { | |
| 65 | - | log.Warnf("Error checking for new version: %s", err) | |
| 66 | - | } else if msg != "" { | |
| 67 | - | log.Info(msg) | |
| 68 | - | } | |
| 69 | - | }() | |
| 56 | + | // Start periodic version checking in the background | |
| 57 | + | go common.CheckVersionPeriodically(buildInfo.BuildVersion, 3*time.Hour) | |
| 70 | 58 | ||
| 71 | 59 | Execute(a, s) | |
| 72 | 60 | } | |
| @@ -2,13 +2,12 @@ package cmd | |||||
| 2 | 2 | ||||
| 3 | import ( | 3 | import ( | ||
| 4 | "fmt" | 4 | "fmt" | ||
| 5 | - | "regexp" | 5 | + | "time" |
| 6 | 6 | ||||
| 7 | "github.com/bnema/gordon/internal/cli" | 7 | "github.com/bnema/gordon/internal/cli" | ||
| 8 | "github.com/bnema/gordon/internal/cli/cmd" | 8 | "github.com/bnema/gordon/internal/cli/cmd" | ||
| 9 | "github.com/bnema/gordon/internal/common" | 9 | "github.com/bnema/gordon/internal/common" | ||
| 10 | "github.com/bnema/gordon/internal/server" | 10 | "github.com/bnema/gordon/internal/server" | ||
| 11 | - | "github.com/charmbracelet/log" | |||
| 12 | "github.com/spf13/cobra" | 11 | "github.com/spf13/cobra" | ||
| 13 | 12 | ||||
| 14 | // env auto load | 13 | // env auto load | ||
| @@ -16,9 +15,6 @@ import ( | |||||
| 16 | ) | 15 | ) | ||
| 17 | 16 | ||||
| 18 | var ( | 17 | var ( | ||
| 19 | - | build string | |||
| 20 | - | commit string | |||
| 21 | - | date string | |||
| 22 | rootCmd = &cobra.Command{Use: "gordon"} | 18 | rootCmd = &cobra.Command{Use: "gordon"} | ||
| 23 | proxyURL = "https://gordon-proxy.bamen.dev" | 19 | proxyURL = "https://gordon-proxy.bamen.dev" | ||
| 24 | ) | 20 | ) | ||
| @@ -38,8 +34,7 @@ func Execute(client *cli.App, server *server.App) { | |||||
| 38 | cobra.CheckErr(rootCmd.Execute()) | 34 | cobra.CheckErr(rootCmd.Execute()) | ||
| 39 | } | 35 | } | ||
| 40 | 36 | ||||
| 41 | - | func ExecuteCLI() { | 37 | + | func ExecuteCLI(build, commit, date string) { |
| 42 | - | build = regexp.MustCompile(`\d+\.\d+\.\d+`).FindString(build) | |||
| 43 | buildInfo := &common.BuildConfig{ | 38 | buildInfo := &common.BuildConfig{ | ||
| 44 | BuildVersion: build, | 39 | BuildVersion: build, | ||
| 45 | BuildCommit: commit, | 40 | BuildCommit: commit, | ||
| @@ -58,15 +53,8 @@ func ExecuteCLI() { | |||||
| 58 | 53 | ||||
| 59 | common.DockerInit(&s.Config.ContainerEngine) | 54 | common.DockerInit(&s.Config.ContainerEngine) | ||
| 60 | 55 | ||||
| 61 | - | // Check for new version | 56 | + | // Start periodic version checking in the background |
| 62 | - | go func() { | 57 | + | go common.CheckVersionPeriodically(buildInfo.BuildVersion, 3*time.Hour) |
| 63 | - | msg, err := common.CheckVersionPeriodically(&s.Config) | |||
| 64 | - | if err != nil { | |||
| 65 | - | log.Warnf("Error checking for new version: %s", err) | |||
| 66 | - | } else if msg != "" { | |||
| 67 | - | log.Info(msg) | |||
| 68 | - | } | |||
| 69 | - | }() | |||
| 70 | 58 | ||||
| 71 | Execute(a, s) | 59 | Execute(a, s) | ||
| 72 | } | 60 | } | ||
M go.mod +1 -0
| @@ -4,6 +4,7 @@ go 1.23 | |||
| 4 | 4 | ||
| 5 | 5 | require ( | |
| 6 | 6 | github.com/AlecAivazis/survey/v2 v2.3.7 | |
| 7 | + | github.com/Masterminds/semver/v3 v3.3.1 | |
| 7 | 8 | github.com/PuerkitoBio/goquery v1.9.2 | |
| 8 | 9 | github.com/charmbracelet/bubbles v0.20.0 | |
| 9 | 10 | github.com/charmbracelet/lipgloss v0.13.0 | |
| @@ -4,6 +4,7 @@ go 1.23 | |||||
| 4 | 4 | ||||
| 5 | require ( | 5 | require ( | ||
| 6 | github.com/AlecAivazis/survey/v2 v2.3.7 | 6 | github.com/AlecAivazis/survey/v2 v2.3.7 | ||
| 7 | + | github.com/Masterminds/semver/v3 v3.3.1 | |||
| 7 | github.com/PuerkitoBio/goquery v1.9.2 | 8 | github.com/PuerkitoBio/goquery v1.9.2 | ||
| 8 | github.com/charmbracelet/bubbles v0.20.0 | 9 | github.com/charmbracelet/bubbles v0.20.0 | ||
| 9 | github.com/charmbracelet/lipgloss v0.13.0 | 10 | github.com/charmbracelet/lipgloss v0.13.0 | ||
M go.sum +2 -0
| @@ -2,6 +2,8 @@ github.com/AlecAivazis/survey/v2 v2.3.7 h1:6I/u8FvytdGsgonrYsVn2t8t4QiRnh6QSTqkk | |||
| 2 | 2 | github.com/AlecAivazis/survey/v2 v2.3.7/go.mod h1:xUTIdE4KCOIjsBAE1JYsUPoCqYdZ1reCfTwbto0Fduo= | |
| 3 | 3 | github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8= | |
| 4 | 4 | github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= | |
| 5 | + | github.com/Masterminds/semver/v3 v3.3.1 h1:QtNSWtVZ3nBfk8mAOu/B6v7FMJ+NHTIgUPi7rj+4nv4= | |
| 6 | + | github.com/Masterminds/semver/v3 v3.3.1/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM= | |
| 5 | 7 | github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= | |
| 6 | 8 | github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= | |
| 7 | 9 | github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2 h1:+vx7roKuyA63nhn5WAunQHLTznkw5W8b1Xc0dNjp83s= | |
| @@ -2,6 +2,8 @@ github.com/AlecAivazis/survey/v2 v2.3.7 h1:6I/u8FvytdGsgonrYsVn2t8t4QiRnh6QSTqkk | |||||
| 2 | github.com/AlecAivazis/survey/v2 v2.3.7/go.mod h1:xUTIdE4KCOIjsBAE1JYsUPoCqYdZ1reCfTwbto0Fduo= | 2 | github.com/AlecAivazis/survey/v2 v2.3.7/go.mod h1:xUTIdE4KCOIjsBAE1JYsUPoCqYdZ1reCfTwbto0Fduo= | ||
| 3 | github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8= | 3 | github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8= | ||
| 4 | github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= | 4 | github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= | ||
| 5 | + | github.com/Masterminds/semver/v3 v3.3.1 h1:QtNSWtVZ3nBfk8mAOu/B6v7FMJ+NHTIgUPi7rj+4nv4= | |||
| 6 | + | github.com/Masterminds/semver/v3 v3.3.1/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM= | |||
| 5 | github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= | 7 | github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= | ||
| 6 | github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= | 8 | github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= | ||
| 7 | github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2 h1:+vx7roKuyA63nhn5WAunQHLTznkw5W8b1Xc0dNjp83s= | 9 | github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2 h1:+vx7roKuyA63nhn5WAunQHLTznkw5W8b1Xc0dNjp83s= | ||
M internal/cli/cmd/version.go +17 -5
| @@ -4,6 +4,7 @@ import ( | |||
| 4 | 4 | "fmt" | |
| 5 | 5 | ||
| 6 | 6 | "github.com/bnema/gordon/internal/cli" | |
| 7 | + | "github.com/bnema/gordon/internal/common" | |
| 7 | 8 | "github.com/spf13/cobra" | |
| 8 | 9 | ) | |
| 9 | 10 | ||
| @@ -12,11 +13,22 @@ func NewVersionCommand(a *cli.App) *cobra.Command { | |||
| 12 | 13 | Use: "version", | |
| 13 | 14 | Short: "Display the version of Gordon", | |
| 14 | 15 | Run: func(cmd *cobra.Command, args []string) { | |
| 15 | - | version := a.Config.Build.BuildVersion | |
| 16 | - | if version == "" { | |
| 17 | - | fmt.Println("Version: devel") | |
| 18 | - | } else { | |
| 19 | - | fmt.Printf("Version: %s\n", version) | |
| 16 | + | info := common.GetVersionInfo( | |
| 17 | + | a.Config.Build.BuildVersion, | |
| 18 | + | a.Config.Build.BuildCommit, | |
| 19 | + | a.Config.Build.BuildDate, | |
| 20 | + | ) | |
| 21 | + | fmt.Println(info.String()) | |
| 22 | + | ||
| 23 | + | // Check for updates | |
| 24 | + | hasUpdate, latestVersion, err := common.CheckForNewVersion(info.Version) | |
| 25 | + | if err != nil { | |
| 26 | + | fmt.Printf("Error checking for updates: %v\n", err) | |
| 27 | + | return | |
| 28 | + | } | |
| 29 | + | ||
| 30 | + | if hasUpdate { | |
| 31 | + | fmt.Printf("\nNew version %s available! Visit: https://github.com/bnema/gordon/releases/latest\n", latestVersion) | |
| 20 | 32 | } | |
| 21 | 33 | }, | |
| 22 | 34 | } | |
| @@ -4,6 +4,7 @@ import ( | |||||
| 4 | "fmt" | 4 | "fmt" | ||
| 5 | 5 | ||||
| 6 | "github.com/bnema/gordon/internal/cli" | 6 | "github.com/bnema/gordon/internal/cli" | ||
| 7 | + | "github.com/bnema/gordon/internal/common" | |||
| 7 | "github.com/spf13/cobra" | 8 | "github.com/spf13/cobra" | ||
| 8 | ) | 9 | ) | ||
| 9 | 10 | ||||
| @@ -12,11 +13,22 @@ func NewVersionCommand(a *cli.App) *cobra.Command { | |||||
| 12 | Use: "version", | 13 | Use: "version", | ||
| 13 | Short: "Display the version of Gordon", | 14 | Short: "Display the version of Gordon", | ||
| 14 | Run: func(cmd *cobra.Command, args []string) { | 15 | Run: func(cmd *cobra.Command, args []string) { | ||
| 15 | - | version := a.Config.Build.BuildVersion | 16 | + | info := common.GetVersionInfo( |
| 16 | - | if version == "" { | 17 | + | a.Config.Build.BuildVersion, |
| 17 | - | fmt.Println("Version: devel") | 18 | + | a.Config.Build.BuildCommit, |
| 18 | - | } else { | 19 | + | a.Config.Build.BuildDate, |
| 19 | - | fmt.Printf("Version: %s\n", version) | 20 | + | ) |
| 21 | + | fmt.Println(info.String()) | |||
| 22 | + | ||||
| 23 | + | // Check for updates | |||
| 24 | + | hasUpdate, latestVersion, err := common.CheckForNewVersion(info.Version) | |||
| 25 | + | if err != nil { | |||
| 26 | + | fmt.Printf("Error checking for updates: %v\n", err) | |||
| 27 | + | return | |||
| 28 | + | } | |||
| 29 | + | ||||
| 30 | + | if hasUpdate { | |||
| 31 | + | fmt.Printf("\nNew version %s available! Visit: https://github.com/bnema/gordon/releases/latest\n", latestVersion) | |||
| 20 | } | 32 | } | ||
| 21 | }, | 33 | }, | ||
| 22 | } | 34 | } | ||
M internal/common/init_config.go +5 -0
| @@ -257,3 +257,8 @@ func (c *HttpConfig) FullDomain() string { | |||
| 257 | 257 | } | |
| 258 | 258 | return c.Domain | |
| 259 | 259 | } | |
| 260 | + | ||
| 261 | + | // GetVersion | |
| 262 | + | func (c *Config) GetVersion() string { | |
| 263 | + | return c.Build.BuildVersion | |
| 264 | + | } | |
| @@ -257,3 +257,8 @@ func (c *HttpConfig) FullDomain() string { | |||||
| 257 | } | 257 | } | ||
| 258 | return c.Domain | 258 | return c.Domain | ||
| 259 | } | 259 | } | ||
| 260 | + | ||||
| 261 | + | // GetVersion | |||
| 262 | + | func (c *Config) GetVersion() string { | |||
| 263 | + | return c.Build.BuildVersion | |||
| 264 | + | } | |||
M internal/common/version.go +122 -95
| @@ -1,3 +1,4 @@ | |||
| 1 | + | // internal/common/version.go | |
| 1 | 2 | package common | |
| 2 | 3 | ||
| 3 | 4 | import ( | |
| @@ -5,147 +6,173 @@ import ( | |||
| 5 | 6 | "fmt" | |
| 6 | 7 | "io" | |
| 7 | 8 | "net/http" | |
| 8 | - | "runtime" | |
| 9 | + | "runtime/debug" | |
| 10 | + | "strings" | |
| 9 | 11 | "time" | |
| 10 | 12 | ||
| 11 | - | "github.com/bnema/gordon/pkg/docker" | |
| 13 | + | "github.com/Masterminds/semver/v3" | |
| 12 | 14 | "github.com/charmbracelet/log" | |
| 13 | 15 | ) | |
| 14 | 16 | ||
| 15 | - | type CurrentVersion struct { | |
| 16 | - | isContainer bool | |
| 17 | - | Version string `json:"version"` | |
| 18 | - | } | |
| 17 | + | const ( | |
| 18 | + | githubAPI = "https://api.github.com/repos/bnema/gordon/releases/latest" | |
| 19 | + | ) | |
| 19 | 20 | ||
| 20 | - | type ArchVersion struct { | |
| 21 | - | Name string `json:"name"` | |
| 21 | + | // Info represents version information | |
| 22 | + | type Info struct { | |
| 23 | + | Version string | |
| 24 | + | Commit string | |
| 25 | + | Date string | |
| 22 | 26 | } | |
| 23 | 27 | ||
| 24 | - | type VersionInfo struct { | |
| 25 | - | Name string `json:"name"` | |
| 28 | + | // GitHubRelease represents the GitHub API response | |
| 29 | + | type GitHubRelease struct { | |
| 30 | + | TagName string `json:"tag_name"` | |
| 31 | + | Name string `json:"name"` | |
| 32 | + | PublishedAt time.Time `json:"published_at"` | |
| 26 | 33 | } | |
| 27 | 34 | ||
| 28 | - | type VersionsResponse struct { | |
| 29 | - | AMD64 string `json:"amd64"` | |
| 30 | - | ARM64 string `json:"arm64"` | |
| 31 | - | } | |
| 35 | + | // GetVersionInfo returns the current version information based on BuildConfig | |
| 36 | + | func GetVersionInfo(buildVersion, buildCommit, buildDate string) Info { | |
| 37 | + | // If version was set in BuildConfig, use that | |
| 38 | + | if buildVersion != "" && buildVersion != "dev" { | |
| 39 | + | return Info{ | |
| 40 | + | Version: buildVersion, | |
| 41 | + | Commit: buildCommit, | |
| 42 | + | Date: buildDate, | |
| 43 | + | } | |
| 44 | + | } | |
| 32 | 45 | ||
| 33 | - | func GetCurrentBuildVersionInfo(c *Config) CurrentVersion { | |
| 34 | - | return CurrentVersion{ | |
| 35 | - | isContainer: docker.IsRunningInContainer(), | |
| 36 | - | Version: c.GetVersion(), | |
| 46 | + | // Try to get version from runtime build info (for go install) | |
| 47 | + | if buildInfo, ok := debug.ReadBuildInfo(); ok { | |
| 48 | + | return Info{ | |
| 49 | + | Version: buildInfo.Main.Version, | |
| 50 | + | Commit: "unknown", | |
| 51 | + | Date: "unknown", | |
| 52 | + | } | |
| 37 | 53 | } | |
| 38 | - | } | |
| 39 | 54 | ||
| 40 | - | func CheckForNewVersion(localVersion, remoteVersion string) string { | |
| 41 | - | if localVersion != remoteVersion { | |
| 42 | - | return "A new version is available" | |
| 55 | + | // Fallback for development builds | |
| 56 | + | return Info{ | |
| 57 | + | Version: "dev", | |
| 58 | + | Commit: "unknown", | |
| 59 | + | Date: "unknown", | |
| 43 | 60 | } | |
| 44 | - | return "" | |
| 45 | 61 | } | |
| 46 | 62 | ||
| 47 | - | func getArch() string { | |
| 48 | - | return runtime.GOARCH | |
| 63 | + | // String returns a formatted version string | |
| 64 | + | func (i Info) String() string { | |
| 65 | + | if i.Version == "dev" { | |
| 66 | + | return "Gordon version dev (development build)" | |
| 67 | + | } | |
| 68 | + | ||
| 69 | + | // Check if it's a dirty version | |
| 70 | + | if strings.Contains(i.Version, "-dirty") { | |
| 71 | + | return fmt.Sprintf("Gordon version %s (commit: %s, built at: %s)", | |
| 72 | + | i.Version, i.Commit, i.Date) | |
| 73 | + | } | |
| 74 | + | ||
| 75 | + | return fmt.Sprintf("Gordon version %s (commit: %s, built at: %s)", | |
| 76 | + | i.Version, i.Commit, i.Date) | |
| 49 | 77 | } | |
| 50 | 78 | ||
| 51 | - | func checkAndUpdateVersion(c *Config) (string, error) { | |
| 52 | - | localVersion := GetCurrentBuildVersionInfo(c) | |
| 53 | - | remoteVersions, err := getRemoteVersion(c) | |
| 54 | - | if err != nil { | |
| 55 | - | return "", err | |
| 79 | + | // CheckForNewVersion checks if a newer version is available on GitHub | |
| 80 | + | func CheckForNewVersion(currentVersion string) (bool, string, error) { | |
| 81 | + | // Don't check for new versions in development builds or dirty builds | |
| 82 | + | if currentVersion == "dev" || strings.Contains(currentVersion, "-dirty") { | |
| 83 | + | return false, "", nil | |
| 56 | 84 | } | |
| 57 | 85 | ||
| 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()) | |
| 86 | + | // Create an HTTP client with timeout | |
| 87 | + | client := &http.Client{ | |
| 88 | + | Timeout: 10 * time.Second, | |
| 66 | 89 | } | |
| 67 | 90 | ||
| 68 | - | // Check if remoteVersion is empty | |
| 69 | - | if remoteVersion == "" { | |
| 70 | - | return "", fmt.Errorf("remote version not available for architecture %s", getArch()) | |
| 91 | + | // Create request with User-Agent header (GitHub API requires this) | |
| 92 | + | req, err := http.NewRequest("GET", githubAPI, nil) | |
| 93 | + | if err != nil { | |
| 94 | + | return false, "", fmt.Errorf("error creating request: %w", err) | |
| 71 | 95 | } | |
| 96 | + | req.Header.Set("User-Agent", "Gordon-Version-Checker") | |
| 72 | 97 | ||
| 73 | - | message := CheckForNewVersion(localVersion.Version, remoteVersion) | |
| 98 | + | // Make the request | |
| 99 | + | resp, err := client.Do(req) | |
| 100 | + | if err != nil { | |
| 101 | + | return false, "", fmt.Errorf("error making request: %w", err) | |
| 102 | + | } | |
| 103 | + | defer resp.Body.Close() | |
| 74 | 104 | ||
| 75 | - | // Return the message without an error, even if it's empty | |
| 76 | - | return message, nil | |
| 77 | - | } | |
| 105 | + | if resp.StatusCode != http.StatusOK { | |
| 106 | + | return false, "", fmt.Errorf("unexpected status code: %d", resp.StatusCode) | |
| 107 | + | } | |
| 78 | 108 | ||
| 79 | - | // GetVersion returns the version | |
| 80 | - | func (c *Config) GetVersion() string { | |
| 81 | - | if c.Build.BuildVersion != "" { | |
| 82 | - | return c.Build.BuildVersion | |
| 109 | + | body, err := io.ReadAll(resp.Body) | |
| 110 | + | if err != nil { | |
| 111 | + | return false, "", fmt.Errorf("error reading response: %w", err) | |
| 83 | 112 | } | |
| 84 | - | return "devel" | |
| 85 | - | } | |
| 86 | 113 | ||
| 87 | - | func CheckVersionPeriodically(c *Config) (string, error) { | |
| 88 | - | // Check if version is "devel" | |
| 89 | - | if c.GetVersion() == "devel" { | |
| 90 | - | return "", nil | |
| 114 | + | var release GitHubRelease | |
| 115 | + | if err := json.Unmarshal(body, &release); err != nil { | |
| 116 | + | return false, "", fmt.Errorf("error parsing JSON: %w", err) | |
| 91 | 117 | } | |
| 92 | 118 | ||
| 93 | - | // Perform an immediate check. | |
| 94 | - | newVersionMessage, err := checkAndUpdateVersion(c) | |
| 119 | + | // Clean up version strings | |
| 120 | + | latestVersion := strings.TrimPrefix(release.TagName, "v") | |
| 121 | + | currentBase := extractBaseVersion(currentVersion) | |
| 95 | 122 | ||
| 123 | + | // Parse versions using semver | |
| 124 | + | current, err := semver.NewVersion(currentBase) | |
| 96 | 125 | if err != nil { | |
| 97 | - | return "", err | |
| 126 | + | return false, "", fmt.Errorf("error parsing current version: %w", err) | |
| 98 | 127 | } | |
| 99 | 128 | ||
| 100 | - | if newVersionMessage != "" { | |
| 101 | - | log.Info(newVersionMessage) | |
| 102 | - | return "", nil | |
| 129 | + | latest, err := semver.NewVersion(latestVersion) | |
| 130 | + | if err != nil { | |
| 131 | + | return false, "", fmt.Errorf("error parsing latest version: %w", err) | |
| 103 | 132 | } | |
| 104 | 133 | ||
| 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 | - | } | |
| 134 | + | // Compare versions | |
| 135 | + | if latest.GreaterThan(current) { | |
| 136 | + | return true, release.TagName, nil | |
| 120 | 137 | } | |
| 121 | 138 | ||
| 122 | - | return "", nil | |
| 139 | + | return false, "", nil | |
| 123 | 140 | } | |
| 124 | 141 | ||
| 125 | - | func getRemoteVersion(c *Config) (VersionsResponse, error) { | |
| 126 | - | proxyURL := c.Build.ProxyURL | |
| 127 | - | if proxyURL == "" { | |
| 128 | - | return VersionsResponse{}, fmt.Errorf("proxy URL is empty") | |
| 129 | - | } | |
| 142 | + | // CheckVersionPeriodically checks for new versions periodically | |
| 143 | + | func CheckVersionPeriodically(currentVersion string, checkInterval time.Duration) { | |
| 144 | + | ticker := time.NewTicker(checkInterval) | |
| 145 | + | defer ticker.Stop() | |
| 130 | 146 | ||
| 131 | - | checkVersionEndpoint := proxyURL + "/version" | |
| 132 | - | resp, err := http.Get(checkVersionEndpoint) | |
| 133 | - | if err != nil { | |
| 134 | - | return VersionsResponse{}, err | |
| 135 | - | } | |
| 147 | + | // Initial check | |
| 148 | + | checkVersion(currentVersion) | |
| 136 | 149 | ||
| 137 | - | defer resp.Body.Close() | |
| 150 | + | // Periodic checks | |
| 151 | + | for range ticker.C { | |
| 152 | + | checkVersion(currentVersion) | |
| 153 | + | } | |
| 154 | + | } | |
| 138 | 155 | ||
| 139 | - | body, err := io.ReadAll(resp.Body) | |
| 156 | + | func checkVersion(currentVersion string) { | |
| 157 | + | hasUpdate, latestVersion, err := CheckForNewVersion(currentVersion) | |
| 140 | 158 | if err != nil { | |
| 141 | - | return VersionsResponse{}, err | |
| 159 | + | log.Error("Failed to check for updates", "error", err) | |
| 160 | + | return | |
| 142 | 161 | } | |
| 143 | 162 | ||
| 144 | - | var remoteVersionInfo VersionsResponse | |
| 145 | - | err = json.Unmarshal(body, &remoteVersionInfo) | |
| 146 | - | if err != nil { | |
| 147 | - | return VersionsResponse{}, err | |
| 163 | + | if hasUpdate { | |
| 164 | + | log.Info("New version available!", | |
| 165 | + | "current", currentVersion, | |
| 166 | + | "latest", latestVersion, | |
| 167 | + | "update_url", "https://github.com/bnema/gordon/releases/latest") | |
| 148 | 168 | } | |
| 169 | + | } | |
| 170 | + | ||
| 171 | + | func extractBaseVersion(version string) string { | |
| 172 | + | // Remove 'v' prefix if present | |
| 173 | + | version = strings.TrimPrefix(version, "v") | |
| 149 | 174 | ||
| 150 | - | return remoteVersionInfo, nil | |
| 175 | + | // Split on first hyphen to remove dirty/commit info | |
| 176 | + | parts := strings.SplitN(version, "-", 2) | |
| 177 | + | return parts[0] | |
| 151 | 178 | } | |
| @@ -1,3 +1,4 @@ | |||||
| 1 | + | // internal/common/version.go | |||
| 1 | package common | 2 | package common | ||
| 2 | 3 | ||||
| 3 | import ( | 4 | import ( | ||
| @@ -5,147 +6,173 @@ import ( | |||||
| 5 | "fmt" | 6 | "fmt" | ||
| 6 | "io" | 7 | "io" | ||
| 7 | "net/http" | 8 | "net/http" | ||
| 8 | - | "runtime" | 9 | + | "runtime/debug" |
| 10 | + | "strings" | |||
| 9 | "time" | 11 | "time" | ||
| 10 | 12 | ||||
| 11 | - | "github.com/bnema/gordon/pkg/docker" | 13 | + | "github.com/Masterminds/semver/v3" |
| 12 | "github.com/charmbracelet/log" | 14 | "github.com/charmbracelet/log" | ||
| 13 | ) | 15 | ) | ||
| 14 | 16 | ||||
| 15 | - | type CurrentVersion struct { | 17 | + | const ( |
| 16 | - | isContainer bool | 18 | + | githubAPI = "https://api.github.com/repos/bnema/gordon/releases/latest" |
| 17 | - | Version string `json:"version"` | 19 | + | ) |
| 18 | - | } | |||
| 19 | 20 | ||||
| 20 | - | type ArchVersion struct { | 21 | + | // Info represents version information |
| 21 | - | Name string `json:"name"` | 22 | + | type Info struct { |
| 23 | + | Version string | |||
| 24 | + | Commit string | |||
| 25 | + | Date string | |||
| 22 | } | 26 | } | ||
| 23 | 27 | ||||
| 24 | - | type VersionInfo struct { | 28 | + | // GitHubRelease represents the GitHub API response |
| 25 | - | Name string `json:"name"` | 29 | + | type GitHubRelease struct { |
| 30 | + | TagName string `json:"tag_name"` | |||
| 31 | + | Name string `json:"name"` | |||
| 32 | + | PublishedAt time.Time `json:"published_at"` | |||
| 26 | } | 33 | } | ||
| 27 | 34 | ||||
| 28 | - | type VersionsResponse struct { | 35 | + | // GetVersionInfo returns the current version information based on BuildConfig |
| 29 | - | AMD64 string `json:"amd64"` | 36 | + | func GetVersionInfo(buildVersion, buildCommit, buildDate string) Info { |
| 30 | - | ARM64 string `json:"arm64"` | 37 | + | // If version was set in BuildConfig, use that |
| 31 | - | } | 38 | + | if buildVersion != "" && buildVersion != "dev" { |
| 39 | + | return Info{ | |||
| 40 | + | Version: buildVersion, | |||
| 41 | + | Commit: buildCommit, | |||
| 42 | + | Date: buildDate, | |||
| 43 | + | } | |||
| 44 | + | } | |||
| 32 | 45 | ||||
| 33 | - | func GetCurrentBuildVersionInfo(c *Config) CurrentVersion { | 46 | + | // Try to get version from runtime build info (for go install) |
| 34 | - | return CurrentVersion{ | 47 | + | if buildInfo, ok := debug.ReadBuildInfo(); ok { |
| 35 | - | isContainer: docker.IsRunningInContainer(), | 48 | + | return Info{ |
| 36 | - | Version: c.GetVersion(), | 49 | + | Version: buildInfo.Main.Version, |
| 50 | + | Commit: "unknown", | |||
| 51 | + | Date: "unknown", | |||
| 52 | + | } | |||
| 37 | } | 53 | } | ||
| 38 | - | } | |||
| 39 | 54 | ||||
| 40 | - | func CheckForNewVersion(localVersion, remoteVersion string) string { | 55 | + | // Fallback for development builds |
| 41 | - | if localVersion != remoteVersion { | 56 | + | return Info{ |
| 42 | - | return "A new version is available" | 57 | + | Version: "dev", |
| 58 | + | Commit: "unknown", | |||
| 59 | + | Date: "unknown", | |||
| 43 | } | 60 | } | ||
| 44 | - | return "" | |||
| 45 | } | 61 | } | ||
| 46 | 62 | ||||
| 47 | - | func getArch() string { | 63 | + | // String returns a formatted version string |
| 48 | - | return runtime.GOARCH | 64 | + | func (i Info) String() string { |
| 65 | + | if i.Version == "dev" { | |||
| 66 | + | return "Gordon version dev (development build)" | |||
| 67 | + | } | |||
| 68 | + | ||||
| 69 | + | // Check if it's a dirty version | |||
| 70 | + | if strings.Contains(i.Version, "-dirty") { | |||
| 71 | + | return fmt.Sprintf("Gordon version %s (commit: %s, built at: %s)", | |||
| 72 | + | i.Version, i.Commit, i.Date) | |||
| 73 | + | } | |||
| 74 | + | ||||
| 75 | + | return fmt.Sprintf("Gordon version %s (commit: %s, built at: %s)", | |||
| 76 | + | i.Version, i.Commit, i.Date) | |||
| 49 | } | 77 | } | ||
| 50 | 78 | ||||
| 51 | - | func checkAndUpdateVersion(c *Config) (string, error) { | 79 | + | // CheckForNewVersion checks if a newer version is available on GitHub |
| 52 | - | localVersion := GetCurrentBuildVersionInfo(c) | 80 | + | func CheckForNewVersion(currentVersion string) (bool, string, error) { |
| 53 | - | remoteVersions, err := getRemoteVersion(c) | 81 | + | // Don't check for new versions in development builds or dirty builds |
| 54 | - | if err != nil { | 82 | + | if currentVersion == "dev" || strings.Contains(currentVersion, "-dirty") { |
| 55 | - | return "", err | 83 | + | return false, "", nil |
| 56 | } | 84 | } | ||
| 57 | 85 | ||||
| 58 | - | var remoteVersion string | 86 | + | // Create an HTTP client with timeout |
| 59 | - | switch getArch() { | 87 | + | client := &http.Client{ |
| 60 | - | case "amd64": | 88 | + | Timeout: 10 * time.Second, |
| 61 | - | remoteVersion = remoteVersions.AMD64 | |||
| 62 | - | case "arm64": | |||
| 63 | - | remoteVersion = remoteVersions.ARM64 | |||
| 64 | - | default: | |||
| 65 | - | return "", fmt.Errorf("unsupported architecture: %s", getArch()) | |||
| 66 | } | 89 | } | ||
| 67 | 90 | ||||
| 68 | - | // Check if remoteVersion is empty | 91 | + | // Create request with User-Agent header (GitHub API requires this) |
| 69 | - | if remoteVersion == "" { | 92 | + | req, err := http.NewRequest("GET", githubAPI, nil) |
| 70 | - | return "", fmt.Errorf("remote version not available for architecture %s", getArch()) | 93 | + | if err != nil { |
| 94 | + | return false, "", fmt.Errorf("error creating request: %w", err) | |||
| 71 | } | 95 | } | ||
| 96 | + | req.Header.Set("User-Agent", "Gordon-Version-Checker") | |||
| 72 | 97 | ||||
| 73 | - | message := CheckForNewVersion(localVersion.Version, remoteVersion) | 98 | + | // Make the request |
| 99 | + | resp, err := client.Do(req) | |||
| 100 | + | if err != nil { | |||
| 101 | + | return false, "", fmt.Errorf("error making request: %w", err) | |||
| 102 | + | } | |||
| 103 | + | defer resp.Body.Close() | |||
| 74 | 104 | ||||
| 75 | - | // Return the message without an error, even if it's empty | 105 | + | if resp.StatusCode != http.StatusOK { |
| 76 | - | return message, nil | 106 | + | return false, "", fmt.Errorf("unexpected status code: %d", resp.StatusCode) |
| 77 | - | } | 107 | + | } |
| 78 | 108 | ||||
| 79 | - | // GetVersion returns the version | 109 | + | body, err := io.ReadAll(resp.Body) |
| 80 | - | func (c *Config) GetVersion() string { | 110 | + | if err != nil { |
| 81 | - | if c.Build.BuildVersion != "" { | 111 | + | return false, "", fmt.Errorf("error reading response: %w", err) |
| 82 | - | return c.Build.BuildVersion | |||
| 83 | } | 112 | } | ||
| 84 | - | return "devel" | |||
| 85 | - | } | |||
| 86 | 113 | ||||
| 87 | - | func CheckVersionPeriodically(c *Config) (string, error) { | 114 | + | var release GitHubRelease |
| 88 | - | // Check if version is "devel" | 115 | + | if err := json.Unmarshal(body, &release); err != nil { |
| 89 | - | if c.GetVersion() == "devel" { | 116 | + | return false, "", fmt.Errorf("error parsing JSON: %w", err) |
| 90 | - | return "", nil | |||
| 91 | } | 117 | } | ||
| 92 | 118 | ||||
| 93 | - | // Perform an immediate check. | 119 | + | // Clean up version strings |
| 94 | - | newVersionMessage, err := checkAndUpdateVersion(c) | 120 | + | latestVersion := strings.TrimPrefix(release.TagName, "v") |
| 121 | + | currentBase := extractBaseVersion(currentVersion) | |||
| 95 | 122 | ||||
| 123 | + | // Parse versions using semver | |||
| 124 | + | current, err := semver.NewVersion(currentBase) | |||
| 96 | if err != nil { | 125 | if err != nil { | ||
| 97 | - | return "", err | 126 | + | return false, "", fmt.Errorf("error parsing current version: %w", err) |
| 98 | } | 127 | } | ||
| 99 | 128 | ||||
| 100 | - | if newVersionMessage != "" { | 129 | + | latest, err := semver.NewVersion(latestVersion) |
| 101 | - | log.Info(newVersionMessage) | 130 | + | if err != nil { |
| 102 | - | return "", nil | 131 | + | return false, "", fmt.Errorf("error parsing latest version: %w", err) |
| 103 | } | 132 | } | ||
| 104 | 133 | ||||
| 105 | - | // Set up a ticker to check every 5 minutes. | 134 | + | // Compare versions |
| 106 | - | timer := time.NewTicker(5 * time.Minute) | 135 | + | if latest.GreaterThan(current) { |
| 107 | - | defer timer.Stop() | 136 | + | return true, release.TagName, nil |
| 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 | } | 137 | } | ||
| 121 | 138 | ||||
| 122 | - | return "", nil | 139 | + | return false, "", nil |
| 123 | } | 140 | } | ||
| 124 | 141 | ||||
| 125 | - | func getRemoteVersion(c *Config) (VersionsResponse, error) { | 142 | + | // CheckVersionPeriodically checks for new versions periodically |
| 126 | - | proxyURL := c.Build.ProxyURL | 143 | + | func CheckVersionPeriodically(currentVersion string, checkInterval time.Duration) { |
| 127 | - | if proxyURL == "" { | 144 | + | ticker := time.NewTicker(checkInterval) |
| 128 | - | return VersionsResponse{}, fmt.Errorf("proxy URL is empty") | 145 | + | defer ticker.Stop() |
| 129 | - | } | |||
| 130 | 146 | ||||
| 131 | - | checkVersionEndpoint := proxyURL + "/version" | 147 | + | // Initial check |
| 132 | - | resp, err := http.Get(checkVersionEndpoint) | 148 | + | checkVersion(currentVersion) |
| 133 | - | if err != nil { | |||
| 134 | - | return VersionsResponse{}, err | |||
| 135 | - | } | |||
| 136 | 149 | ||||
| 137 | - | defer resp.Body.Close() | 150 | + | // Periodic checks |
| 151 | + | for range ticker.C { | |||
| 152 | + | checkVersion(currentVersion) | |||
| 153 | + | } | |||
| 154 | + | } | |||
| 138 | 155 | ||||
| 139 | - | body, err := io.ReadAll(resp.Body) | 156 | + | func checkVersion(currentVersion string) { |
| 157 | + | hasUpdate, latestVersion, err := CheckForNewVersion(currentVersion) | |||
| 140 | if err != nil { | 158 | if err != nil { | ||
| 141 | - | return VersionsResponse{}, err | 159 | + | log.Error("Failed to check for updates", "error", err) |
| 160 | + | return | |||
| 142 | } | 161 | } | ||
| 143 | 162 | ||||
| 144 | - | var remoteVersionInfo VersionsResponse | 163 | + | if hasUpdate { |
| 145 | - | err = json.Unmarshal(body, &remoteVersionInfo) | 164 | + | log.Info("New version available!", |
| 146 | - | if err != nil { | 165 | + | "current", currentVersion, |
| 147 | - | return VersionsResponse{}, err | 166 | + | "latest", latestVersion, |
| 167 | + | "update_url", "https://github.com/bnema/gordon/releases/latest") | |||
| 148 | } | 168 | } | ||
| 169 | + | } | |||
| 170 | + | ||||
| 171 | + | func extractBaseVersion(version string) string { | |||
| 172 | + | // Remove 'v' prefix if present | |||
| 173 | + | version = strings.TrimPrefix(version, "v") | |||
| 149 | 174 | ||||
| 150 | - | return remoteVersionInfo, nil | 175 | + | // Split on first hyphen to remove dirty/commit info |
| 176 | + | parts := strings.SplitN(version, "-", 2) | |||
| 177 | + | return parts[0] | |||
| 151 | } | 178 | } | ||
M main.go +9 -1
| @@ -2,8 +2,16 @@ package main | |||
| 2 | 2 | ||
| 3 | 3 | import ( | |
| 4 | 4 | "github.com/bnema/gordon/cmd" | |
| 5 | + | "github.com/charmbracelet/log" | |
| 6 | + | ) | |
| 7 | + | ||
| 8 | + | var ( | |
| 9 | + | version string | |
| 10 | + | commit string | |
| 11 | + | date string | |
| 5 | 12 | ) | |
| 6 | 13 | ||
| 7 | 14 | func main() { | |
| 8 | - | cmd.ExecuteCLI() | |
| 15 | + | log.Info("Starting Gordon", "version", version, "commit", commit, "date", date) | |
| 16 | + | cmd.ExecuteCLI(version, commit, date) | |
| 9 | 17 | } | |
| @@ -2,8 +2,16 @@ package main | |||||
| 2 | 2 | ||||
| 3 | import ( | 3 | import ( | ||
| 4 | "github.com/bnema/gordon/cmd" | 4 | "github.com/bnema/gordon/cmd" | ||
| 5 | + | "github.com/charmbracelet/log" | |||
| 6 | + | ) | |||
| 7 | + | ||||
| 8 | + | var ( | |||
| 9 | + | version string | |||
| 10 | + | commit string | |||
| 11 | + | date string | |||
| 5 | ) | 12 | ) | ||
| 6 | 13 | ||||
| 7 | func main() { | 14 | func main() { | ||
| 8 | - | cmd.ExecuteCLI() | 15 | + | log.Info("Starting Gordon", "version", version, "commit", commit, "date", date) |
| 16 | + | cmd.ExecuteCLI(version, commit, date) | |||
| 9 | } | 17 | } | ||