@@ -6,18 +6,37 @@ package version
66
77import (
88 "context"
9+ "encoding/json"
10+ "fmt"
11+ "os"
12+ "path/filepath"
13+ "strings"
14+ "time"
915
1016 "github.com/Masterminds/semver/v3"
1117
18+ "github.com/elastic/elastic-package/internal/configuration/locations"
19+ "github.com/elastic/elastic-package/internal/environment"
1220 "github.com/elastic/elastic-package/internal/github"
1321 "github.com/elastic/elastic-package/internal/logger"
1422)
1523
1624const (
1725 repositoryOwner = "elastic"
1826 repositoryName = "elastic-package"
27+
28+ latestVersionFile = "latestVersion"
29+ defaultCacheDuration = 30 * time .Minute
1930)
2031
32+ var checkUpdatedDisabledEnv = environment .WithElasticPackagePrefix ("CHECK_UPDATE_DISABLED" )
33+
34+ type versionLatest struct {
35+ TagName string `json:"tag"`
36+ HtmlURL string `json:"html_url"`
37+ Timestamp time.Time `json:"timestamp"`
38+ }
39+
2140// CheckUpdate function checks using Github Release API if newer version is available.
2241func CheckUpdate () {
2342 if Tag == "" {
@@ -26,16 +45,39 @@ func CheckUpdate() {
2645 return
2746 }
2847
29- githubClient := github .UnauthorizedClient ()
30- release , _ , err := githubClient .Repositories .GetLatestRelease (context .TODO (), repositoryOwner , repositoryName )
31- if err != nil {
32- logger .Debugf ("Error: can't check latest release, %v" , err )
48+ v , ok := os .LookupEnv (checkUpdatedDisabledEnv )
49+ if ok && strings .ToLower (v ) != "false" {
50+ logger .Debug ("Disabled checking updates" )
3351 return
3452 }
3553
36- if release .TagName == nil || * release .TagName == "" {
37- logger .Debugf ("Error: release tag is empty" )
38- return
54+ expired := true
55+ latestVersion , err := loadCacheLatestVersion ()
56+ switch {
57+ case err != nil :
58+ logger .Debug ("failed to load latest version from cache: %v" , err )
59+ default :
60+ expired = checkCachedLatestVersion (latestVersion , defaultCacheDuration )
61+ }
62+
63+ var release * versionLatest
64+ switch {
65+ case ! expired :
66+ logger .Debugf ("latest version (cached): %s" , latestVersion )
67+ release = latestVersion
68+ default :
69+ logger .Debugf ("checking latest release in Github" )
70+ githubClient := github .UnauthorizedClient ()
71+ githubRelease , err := githubClient .LatestRelease (context .TODO (), repositoryOwner , repositoryName )
72+ if err != nil {
73+ logger .Debugf ("Error: %v" , err )
74+ return
75+ }
76+ release = & versionLatest {
77+ TagName : * githubRelease .TagName ,
78+ HtmlURL : * githubRelease .HTMLURL ,
79+ Timestamp : time .Now (),
80+ }
3981 }
4082
4183 currentVersion , err := semver .NewVersion (Tag [1 :]) // strip "v" prefix
@@ -44,13 +86,70 @@ func CheckUpdate() {
4486 return
4587 }
4688
47- releaseVersion , err := semver .NewVersion (( * release .TagName ) [1 :]) // strip "v" prefix
89+ releaseVersion , err := semver .NewVersion (release .TagName [1 :]) // strip "v" prefix
4890 if err != nil {
4991 logger .Debugf ("Error: can't parse current version tag, %v" , err )
5092 return
5193 }
5294
5395 if currentVersion .LessThan (releaseVersion ) {
54- logger .Infof ("New version is available - %s. Download from: %s" , * release .TagName , * release .HTMLURL )
96+ logger .Infof ("New version is available - %s. Download from: %s" , release .TagName , release .HtmlURL )
97+ }
98+
99+ // if version cached is not expired, do not write contents into file
100+ if ! expired {
101+ return
102+ }
103+
104+ if err := writeLatestReleaseToCache (release ); err != nil {
105+ logger .Debugf ("failed to write latest versoin to cache file: %v" , err )
106+ }
107+ }
108+
109+ func writeLatestReleaseToCache (release * versionLatest ) error {
110+ elasticPackagePath , err := locations .NewLocationManager ()
111+ if err != nil {
112+ return fmt .Errorf ("failed locating the configuration directory: %w" , err )
113+ }
114+
115+ latestVersionPath := filepath .Join (elasticPackagePath .RootDir (), latestVersionFile )
116+
117+ contents , err := json .Marshal (release )
118+ if err != nil {
119+ return fmt .Errorf ("failed to encode file %s: %w" , latestVersionPath , err )
120+ }
121+ err = os .WriteFile (latestVersionPath , contents , 0644 )
122+ if err != nil {
123+ return fmt .Errorf ("writing file failed (path: %s): %w" , latestVersionPath , err )
124+ }
125+
126+ return nil
127+ }
128+
129+ func loadCacheLatestVersion () (* versionLatest , error ) {
130+ elasticPackagePath , err := locations .NewLocationManager ()
131+ if err != nil {
132+ return nil , fmt .Errorf ("failed locating the configuration directory: %w" , err )
55133 }
134+
135+ latestVersionPath := filepath .Join (elasticPackagePath .RootDir (), latestVersionFile )
136+ contents , err := os .ReadFile (latestVersionPath )
137+ if err != nil {
138+ logger .Warnf ("reading version file failed: %w" , err .Error ())
139+ return nil , fmt .Errorf ("reading version file failed: %w" , err )
140+ }
141+
142+ var infoVersion versionLatest
143+ err = json .Unmarshal (contents , & infoVersion )
144+ if err != nil {
145+ return nil , fmt .Errorf ("failed to decode file %s: %w" , latestVersionPath , err )
146+ }
147+
148+ return & infoVersion , nil
149+ }
150+
151+ func checkCachedLatestVersion (latest * versionLatest , expiration time.Duration ) bool {
152+ exprirationTime := time .Now ().Add (- expiration )
153+
154+ return latest .Timestamp .Before (exprirationTime )
56155}
0 commit comments