@@ -2,32 +2,111 @@ package update
22
33import (
44 "fmt"
5+ "io/ioutil"
6+ "time"
57
68 "github.com/github/gh-cli/api"
79 "github.com/hashicorp/go-version"
10+ "github.com/mitchellh/go-homedir"
11+ "gopkg.in/yaml.v3"
812)
913
1014// ReleaseInfo stores information about a release
1115type ReleaseInfo struct {
12- Version string `json:"tag_name"`
13- URL string `json:"html_url"`
16+ Version string
17+ URL string
18+ }
19+
20+ type StateEntry struct {
21+ CheckedForUpdateAt time.Time `yaml:"checked_for_update_at"`
22+ LatestRelease ReleaseInfo `yaml:"latest_release"`
1423}
1524
1625// CheckForUpdate checks whether this software has had a newer relase on GitHub
1726func CheckForUpdate (client * api.Client , repo , currentVersion string ) (* ReleaseInfo , error ) {
18- latestRelease := ReleaseInfo {}
19- err := client .REST ("GET" , fmt .Sprintf ("repos/%s/releases/latest" , repo ), nil , & latestRelease )
27+ latestRelease , err := getLatestReleaseInfo (client , repo , currentVersion )
2028 if err != nil {
2129 return nil , err
2230 }
2331
2432 if versionGreaterThan (latestRelease .Version , currentVersion ) {
25- return & latestRelease , nil
33+ return latestRelease , nil
2634 }
2735
2836 return nil , nil
2937}
3038
39+ func getLatestReleaseInfo (client * api.Client , repo , currentVersion string ) (* ReleaseInfo , error ) {
40+ stateEntry , err := getStateEntry ()
41+ if err != nil {
42+ return nil , err
43+ }
44+
45+ checkedRecently := time .Since (stateEntry .CheckedForUpdateAt ).Hours () < 24
46+ if checkedRecently {
47+ return & stateEntry .LatestRelease , nil
48+ }
49+
50+ latestRelease := ReleaseInfo {}
51+ err = client .REST ("GET" , fmt .Sprintf ("repos/%s/releases/latest" , repo ), nil , & latestRelease )
52+ if err != nil {
53+ return nil , err
54+ }
55+
56+ err = setStateEntry (time .Now (), latestRelease )
57+ if err != nil {
58+ return nil , err
59+ }
60+
61+ return & latestRelease , nil
62+ }
63+
64+ func stateFileName () string {
65+ f , _ := homedir .Expand ("~/.config/gh/state.yml" )
66+ return f
67+ }
68+
69+ func getStateEntry () (* StateEntry , error ) {
70+ f := stateFileName ()
71+ content , err := ioutil .ReadFile (f )
72+ if err != nil {
73+ data := StateEntry {
74+ CheckedForUpdateAt : time .Now (),
75+ LatestRelease : ReleaseInfo {
76+ Version : "v0.0.0" ,
77+ URL : "<?>" ,
78+ },
79+ }
80+ content , err = yaml .Marshal (data )
81+ if err != nil {
82+ return nil , err
83+ }
84+ ioutil .WriteFile (f , content , 0600 )
85+ }
86+
87+ var stateEntry StateEntry
88+ err = yaml .Unmarshal (content , & stateEntry )
89+ if err != nil {
90+ return nil , err
91+ }
92+
93+ return & stateEntry , nil
94+ }
95+
96+ func setStateEntry (t time.Time , r ReleaseInfo ) error {
97+ data := StateEntry {
98+ CheckedForUpdateAt : t ,
99+ LatestRelease : r ,
100+ }
101+ content , err := yaml .Marshal (data )
102+ if err != nil {
103+ return err
104+ }
105+ ioutil .WriteFile (stateFileName (), content , 0600 )
106+
107+ return nil
108+ }
109+
31110func versionGreaterThan (v , w string ) bool {
32111 vv , ve := version .NewVersion (v )
33112 vw , we := version .NewVersion (w )
0 commit comments