@@ -11,6 +11,7 @@ import (
1111 "os/exec"
1212 "runtime"
1313 "strings"
14+ "time"
1415
1516 "github.com/fatih/color"
1617 "github.com/rs/zerolog/log"
@@ -24,36 +25,42 @@ func CheckForUpdateWithWriter(w io.Writer) {
2425 if checkEnv := os .Getenv ("INFISICAL_DISABLE_UPDATE_CHECK" ); checkEnv != "" {
2526 return
2627 }
27- latestVersion , _ , err := getLatestTag ("Infisical" , "cli" )
28+ latestVersion , _ , isUrgent , err := getLatestTag ("Infisical" , "cli" )
2829 if err != nil {
2930 log .Debug ().Err (err )
3031 // do nothing and continue
3132 return
3233 }
3334
34- // daysSinceRelease, _ := daysSinceDate(publishedDate)
35+ if latestVersion == CLI_VERSION {
36+ return
37+ }
3538
36- if latestVersion != CLI_VERSION {
37- yellow := color .New (color .FgYellow ).SprintFunc ()
38- blue := color .New (color .FgCyan ).SprintFunc ()
39- black := color .New (color .FgBlack ).SprintFunc ()
39+ // Only prompt if the user's current version is at least 48 hours old, unless urgent.
40+ // This avoids nagging users who recently updated.
41+ currentVersionPublishedAt , err := getReleasePublishedAt ("Infisical" , "cli" , CLI_VERSION )
42+ if err == nil && ! isUrgent && time .Since (currentVersionPublishedAt ).Hours () < 48 {
43+ return
44+ }
4045
41- msg := fmt .Sprintf ("%s %s %s %s" ,
42- yellow ("A new release of infisical is available:" ),
43- blue (CLI_VERSION ),
44- black ("->" ),
45- blue (latestVersion ),
46- )
46+ yellow := color .New (color .FgYellow ).SprintFunc ()
47+ blue := color .New (color .FgCyan ).SprintFunc ()
48+ black := color .New (color .FgBlack ).SprintFunc ()
4749
48- fmt .Fprintln (w , msg )
50+ msg := fmt .Sprintf ("%s %s %s %s" ,
51+ yellow ("A new release of infisical is available:" ),
52+ blue (CLI_VERSION ),
53+ black ("->" ),
54+ blue (latestVersion ),
55+ )
4956
50- updateInstructions := GetUpdateInstructions ( )
57+ fmt . Fprintln ( w , msg )
5158
52- if updateInstructions != "" {
53- msg = fmt .Sprintf ("\n %s\n " , GetUpdateInstructions ())
54- fmt .Fprintln (w , msg )
55- }
59+ updateInstructions := GetUpdateInstructions ()
5660
61+ if updateInstructions != "" {
62+ msg = fmt .Sprintf ("\n %s\n " , GetUpdateInstructions ())
63+ fmt .Fprintln (w , msg )
5764 }
5865}
5966
@@ -80,38 +87,80 @@ func DisplayAptInstallationChangeBannerWithWriter(isSilent bool, w io.Writer) {
8087 }
8188}
8289
83- func getLatestTag (repoOwner string , repoName string ) (string , string , error ) {
90+ func getLatestTag (repoOwner string , repoName string ) (string , time. Time , bool , error ) {
8491 url := fmt .Sprintf ("https://api.github.com/repos/%s/%s/releases/latest" , repoOwner , repoName )
8592 resp , err := http .Get (url )
8693 if err != nil {
87- return "" , "" , err
94+ return "" , time. Time {}, false , err
8895 }
8996 if resp .StatusCode != 200 {
90- return "" , "" , errors .New (fmt .Sprintf ("gitHub API returned status code %d" , resp .StatusCode ))
97+ return "" , time. Time {}, false , errors .New (fmt .Sprintf ("gitHub API returned status code %d" , resp .StatusCode ))
9198 }
9299
93100 defer resp .Body .Close ()
94101
95102 body , err := io .ReadAll (resp .Body )
96103 if err != nil {
97- return "" , "" , err
104+ return "" , time. Time {}, false , err
98105 }
99106
100107 var releaseDetails struct {
101108 TagName string `json:"tag_name"`
102109 PublishedAt string `json:"published_at"`
110+ Body string `json:"body"`
103111 }
104112
105113 if err := json .Unmarshal (body , & releaseDetails ); err != nil {
106- return "" , "" , fmt .Errorf ("failed to unmarshal github response: %w" , err )
114+ return "" , time. Time {}, false , fmt .Errorf ("failed to unmarshal github response: %w" , err )
107115 }
108116
117+ publishedAt , err := time .Parse (time .RFC3339 , releaseDetails .PublishedAt )
118+ if err != nil {
119+ return "" , time.Time {}, false , fmt .Errorf ("failed to parse release time: %w" , err )
120+ }
121+
122+ isUrgent := strings .Contains (releaseDetails .Body , "#urgent" )
123+
109124 tag_prefix := "v"
110125
111126 // Extract the version from the first valid tag
112127 version := strings .TrimPrefix (releaseDetails .TagName , tag_prefix )
113128
114- return version , releaseDetails .PublishedAt , nil
129+ return version , publishedAt , isUrgent , nil
130+ }
131+
132+ func getReleasePublishedAt (repoOwner string , repoName string , version string ) (time.Time , error ) {
133+ tag := "v" + version
134+ url := fmt .Sprintf ("https://api.github.com/repos/%s/%s/releases/tags/%s" , repoOwner , repoName , tag )
135+ resp , err := http .Get (url )
136+ if err != nil {
137+ return time.Time {}, err
138+ }
139+ if resp .StatusCode != 200 {
140+ return time.Time {}, errors .New (fmt .Sprintf ("gitHub API returned status code %d" , resp .StatusCode ))
141+ }
142+
143+ defer resp .Body .Close ()
144+
145+ body , err := io .ReadAll (resp .Body )
146+ if err != nil {
147+ return time.Time {}, err
148+ }
149+
150+ var releaseDetails struct {
151+ PublishedAt string `json:"published_at"`
152+ }
153+
154+ if err := json .Unmarshal (body , & releaseDetails ); err != nil {
155+ return time.Time {}, fmt .Errorf ("failed to unmarshal github response: %w" , err )
156+ }
157+
158+ publishedAt , err := time .Parse (time .RFC3339 , releaseDetails .PublishedAt )
159+ if err != nil {
160+ return time.Time {}, fmt .Errorf ("failed to parse release time: %w" , err )
161+ }
162+
163+ return publishedAt , nil
115164}
116165
117166func GetUpdateInstructions () string {
@@ -176,16 +225,3 @@ func IsRunningInDocker() bool {
176225
177226 return strings .Contains (string (cgroup ), "docker" )
178227}
179-
180- // func daysSinceDate(dateString string) (int, error) {
181- // layout := "2006-01-02T15:04:05Z"
182- // parsedDate, err := time.Parse(layout, dateString)
183- // if err != nil {
184- // return 0, err
185- // }
186-
187- // currentTime := time.Now()
188- // difference := currentTime.Sub(parsedDate)
189- // days := int(difference.Hours() / 24)
190- // return days, nil
191- // }
0 commit comments