diff --git a/main.go b/main.go index 2d9ea037d..f09edb18d 100644 --- a/main.go +++ b/main.go @@ -10,6 +10,7 @@ import ( "github.com/github/gh-cli/utils" "github.com/mattn/go-isatty" "github.com/mgutz/ansi" + "github.com/mitchellh/go-homedir" ) var updaterEnabled = "" @@ -60,5 +61,9 @@ func checkForUpdate(currentVersion string) (*update.ReleaseInfo, error) { } repo := updaterEnabled - return update.CheckForUpdate(client, repo, currentVersion) + stateFilePath, err := homedir.Expand("~/.config/gh/state.yml") + if err != nil { + return nil, err + } + return update.CheckForUpdate(client, stateFilePath, repo, currentVersion) } diff --git a/update/update.go b/update/update.go index a1b9d49a0..4264ea23c 100644 --- a/update/update.go +++ b/update/update.go @@ -2,9 +2,12 @@ package update import ( "fmt" + "io/ioutil" + "time" "github.com/github/gh-cli/api" "github.com/hashicorp/go-version" + "gopkg.in/yaml.v3" ) // ReleaseInfo stores information about a release @@ -13,21 +16,71 @@ type ReleaseInfo struct { URL string `json:"html_url"` } +type StateEntry struct { + CheckedForUpdateAt time.Time `yaml:"checked_for_update_at"` + LatestRelease ReleaseInfo `yaml:"latest_release"` +} + // CheckForUpdate checks whether this software has had a newer relase on GitHub -func CheckForUpdate(client *api.Client, repo, currentVersion string) (*ReleaseInfo, error) { - latestRelease := ReleaseInfo{} - err := client.REST("GET", fmt.Sprintf("repos/%s/releases/latest", repo), nil, &latestRelease) +func CheckForUpdate(client *api.Client, stateFilePath, repo, currentVersion string) (*ReleaseInfo, error) { + latestRelease, err := getLatestReleaseInfo(client, stateFilePath, repo, currentVersion) if err != nil { return nil, err } if versionGreaterThan(latestRelease.Version, currentVersion) { - return &latestRelease, nil + return latestRelease, nil } return nil, nil } +func getLatestReleaseInfo(client *api.Client, stateFilePath, repo, currentVersion string) (*ReleaseInfo, error) { + stateEntry, err := getStateEntry(stateFilePath) + if err == nil && time.Since(stateEntry.CheckedForUpdateAt).Hours() < 24 { + return &stateEntry.LatestRelease, nil + } + + latestRelease := ReleaseInfo{} + err = client.REST("GET", fmt.Sprintf("repos/%s/releases/latest", repo), nil, &latestRelease) + if err != nil { + return nil, err + } + + err = setStateEntry(stateFilePath, time.Now(), latestRelease) + if err != nil { + return nil, err + } + + return &latestRelease, nil +} + +func getStateEntry(stateFilePath string) (*StateEntry, error) { + content, err := ioutil.ReadFile(stateFilePath) + if err != nil { + return nil, err + } + + var stateEntry StateEntry + err = yaml.Unmarshal(content, &stateEntry) + if err != nil { + return nil, err + } + + return &stateEntry, nil +} + +func setStateEntry(stateFilePath string, t time.Time, r ReleaseInfo) error { + data := StateEntry{CheckedForUpdateAt: t, LatestRelease: r} + content, err := yaml.Marshal(data) + if err != nil { + return err + } + ioutil.WriteFile(stateFilePath, content, 0600) + + return nil +} + func versionGreaterThan(v, w string) bool { vv, ve := version.NewVersion(v) vw, we := version.NewVersion(w) diff --git a/update/update_test.go b/update/update_test.go index b5e8db7a4..0fdfb4e29 100644 --- a/update/update_test.go +++ b/update/update_test.go @@ -3,6 +3,9 @@ package update import ( "bytes" "fmt" + "io/ioutil" + "log" + "os" "testing" "github.com/github/gh-cli/api" @@ -55,7 +58,7 @@ func TestCheckForUpdate(t *testing.T) { "html_url": "%s" }`, s.LatestVersion, s.LatestURL))) - rel, err := CheckForUpdate(client, "OWNER/REPO", s.CurrentVersion) + rel, err := CheckForUpdate(client, tempFilePath(), "OWNER/REPO", s.CurrentVersion) if err != nil { t.Fatal(err) } @@ -87,3 +90,12 @@ func TestCheckForUpdate(t *testing.T) { }) } } + +func tempFilePath() string { + file, err := ioutil.TempFile("", "") + if err != nil { + log.Fatal(err) + } + os.Remove(file.Name()) + return file.Name() +}