@@ -2,11 +2,15 @@ package main
22
33import (
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