Skip to content

Commit 3a888bd

Browse files
authored
Add auto-update check and notification system (#11)
* Add auto-update check and notification system * fix lint
1 parent 3f69a39 commit 3a888bd

2 files changed

Lines changed: 102 additions & 1 deletion

File tree

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ PACKAGE := github.com/strahe/bwh
77
MAIN_PACKAGE := ./cmd/bwh
88

99
# Build information
10-
VERSION ?= $(shell git describe --tags --always --dirty 2>/dev/null || echo "dev")
10+
VERSION ?= $(or $(BWH_VERSION),$(shell git describe --tags --always --dirty 2>/dev/null || echo "dev"))
1111
BUILD_TIME := $(shell date -u '+%Y-%m-%d_%H:%M:%S_UTC')
1212
COMMIT_HASH := $(shell git rev-parse --short HEAD 2>/dev/null || echo "unknown")
1313

cmd/bwh/main.go

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,15 @@ package main
22

33
import (
44
"context"
5+
"encoding/json"
56
"fmt"
67
"log"
78
"os"
9+
"path/filepath"
10+
"time"
811

912
"github.com/strahe/bwh/internal/config"
13+
"github.com/strahe/bwh/internal/updater"
1014
"github.com/strahe/bwh/internal/version"
1115
"github.com/urfave/cli/v3"
1216
)
@@ -18,6 +22,8 @@ func main() {
1822
Version: version.GetVersion(),
1923
EnableShellCompletion: true,
2024
ShellComplete: shellComplete,
25+
Before: showUpdateNotificationHook,
26+
After: checkForUpdatesHook,
2127
Flags: []cli.Flag{
2228
&cli.StringFlag{
2329
Name: "config",
@@ -79,3 +85,98 @@ func shellComplete(ctx context.Context, cmd *cli.Command) {
7985
}
8086
}
8187
}
88+
89+
func showUpdateNotificationHook(ctx context.Context, cmd *cli.Command) (context.Context, error) {
90+
if len(os.Args) > 1 && os.Args[1] == "update" {
91+
return ctx, nil
92+
}
93+
showCachedUpdateNotification()
94+
return ctx, nil
95+
}
96+
97+
func checkForUpdatesHook(ctx context.Context, cmd *cli.Command) error {
98+
if len(os.Args) > 1 && os.Args[1] == "update" {
99+
return nil
100+
}
101+
102+
if !shouldCheckForUpdates() {
103+
return nil
104+
}
105+
106+
updateLastCheckTime()
107+
108+
checkCtx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
109+
defer cancel()
110+
111+
if info, err := updater.CheckForUpdatesWithTimeout(checkCtx, 2*time.Second); err == nil && info.HasUpdate {
112+
cacheUpdateInfo(info)
113+
}
114+
115+
return nil
116+
}
117+
118+
func shouldCheckForUpdates() bool {
119+
lastCheckFile := getLastCheckFilePath()
120+
if stat, err := os.Stat(lastCheckFile); err == nil {
121+
if time.Since(stat.ModTime()) < 24*time.Hour {
122+
return false
123+
}
124+
}
125+
return true
126+
}
127+
128+
func showCachedUpdateNotification() {
129+
updateCacheFile := getUpdateCacheFilePath()
130+
data, err := os.ReadFile(updateCacheFile)
131+
if err != nil {
132+
return
133+
}
134+
135+
var info updater.UpdateInfo
136+
if err := json.Unmarshal(data, &info); err != nil {
137+
os.Remove(updateCacheFile) //nolint:errcheck
138+
return
139+
}
140+
141+
// Check if user has already upgraded
142+
currentVersion := version.GetVersion()
143+
if updater.CompareVersions(currentVersion, info.LatestVersion) >= 0 {
144+
os.Remove(updateCacheFile) //nolint:errcheck
145+
return
146+
}
147+
148+
fmt.Fprintf(os.Stderr, "\n┌─────────────────────────────────────────────────────────────┐\n")
149+
fmt.Fprintf(os.Stderr, "│ 🎉 BWH CLI %s is available! Current: %-15s │\n", info.LatestVersion, info.CurrentVersion)
150+
fmt.Fprintf(os.Stderr, "│ Run 'bwh update' to upgrade │\n")
151+
fmt.Fprintf(os.Stderr, "└─────────────────────────────────────────────────────────────┘\n\n")
152+
}
153+
154+
func cacheUpdateInfo(info *updater.UpdateInfo) {
155+
updateCacheFile := getUpdateCacheFilePath()
156+
if err := os.MkdirAll(filepath.Dir(updateCacheFile), 0o755); err != nil {
157+
return
158+
}
159+
data, err := json.Marshal(info)
160+
if err != nil {
161+
return
162+
}
163+
os.WriteFile(updateCacheFile, data, 0o644) //nolint:errcheck
164+
}
165+
166+
func updateLastCheckTime() {
167+
lastCheckFile := getLastCheckFilePath()
168+
if err := os.MkdirAll(filepath.Dir(lastCheckFile), 0o755); err != nil {
169+
return
170+
}
171+
os.WriteFile(lastCheckFile, []byte(time.Now().Format(time.RFC3339)), 0o644) //nolint:errcheck
172+
}
173+
174+
func getUpdateCacheFilePath() string {
175+
homeDir, _ := os.UserHomeDir()
176+
return filepath.Join(homeDir, ".bwh", ".update_available")
177+
}
178+
179+
func getLastCheckFilePath() string {
180+
homeDir, _ := os.UserHomeDir()
181+
return filepath.Join(homeDir, ".bwh", ".last_check")
182+
}

0 commit comments

Comments
 (0)