github.com/abdfnx/gh-api@v0.0.0-20210414084727-f5432eec23b8/internal/update/update.go (about) 1 package update 2 3 import ( 4 "fmt" 5 "io/ioutil" 6 "regexp" 7 "strconv" 8 "strings" 9 "time" 10 11 "github.com/abdfnx/gh-api/api" 12 "github.com/abdfnx/gh-api/internal/ghinstance" 13 "github.com/hashicorp/go-version" 14 "gopkg.in/yaml.v3" 15 ) 16 17 var gitDescribeSuffixRE = regexp.MustCompile(`\d+-\d+-g[a-f0-9]{8}$`) 18 19 // ReleaseInfo stores information about a release 20 type ReleaseInfo struct { 21 Version string `json:"tag_name"` 22 URL string `json:"html_url"` 23 PublishedAt time.Time `json:"published_at"` 24 } 25 26 type StateEntry struct { 27 CheckedForUpdateAt time.Time `yaml:"checked_for_update_at"` 28 LatestRelease ReleaseInfo `yaml:"latest_release"` 29 } 30 31 // CheckForUpdate checks whether this software has had a newer release on GitHub 32 func CheckForUpdate(client *api.Client, stateFilePath, repo, currentVersion string) (*ReleaseInfo, error) { 33 stateEntry, _ := getStateEntry(stateFilePath) 34 if stateEntry != nil && time.Since(stateEntry.CheckedForUpdateAt).Hours() < 24 { 35 return nil, nil 36 } 37 38 releaseInfo, err := getLatestReleaseInfo(client, repo) 39 if err != nil { 40 return nil, err 41 } 42 43 err = setStateEntry(stateFilePath, time.Now(), *releaseInfo) 44 if err != nil { 45 return nil, err 46 } 47 48 if versionGreaterThan(releaseInfo.Version, currentVersion) { 49 return releaseInfo, nil 50 } 51 52 return nil, nil 53 } 54 55 func getLatestReleaseInfo(client *api.Client, repo string) (*ReleaseInfo, error) { 56 var latestRelease ReleaseInfo 57 err := client.REST(ghinstance.Default(), "GET", fmt.Sprintf("repos/%s/releases/latest", repo), nil, &latestRelease) 58 if err != nil { 59 return nil, err 60 } 61 62 return &latestRelease, nil 63 } 64 65 func getStateEntry(stateFilePath string) (*StateEntry, error) { 66 content, err := ioutil.ReadFile(stateFilePath) 67 if err != nil { 68 return nil, err 69 } 70 71 var stateEntry StateEntry 72 err = yaml.Unmarshal(content, &stateEntry) 73 if err != nil { 74 return nil, err 75 } 76 77 return &stateEntry, nil 78 } 79 80 func setStateEntry(stateFilePath string, t time.Time, r ReleaseInfo) error { 81 data := StateEntry{CheckedForUpdateAt: t, LatestRelease: r} 82 content, err := yaml.Marshal(data) 83 if err != nil { 84 return err 85 } 86 _ = ioutil.WriteFile(stateFilePath, content, 0600) 87 88 return nil 89 } 90 91 func versionGreaterThan(v, w string) bool { 92 w = gitDescribeSuffixRE.ReplaceAllStringFunc(w, func(m string) string { 93 idx := strings.IndexRune(m, '-') 94 n, _ := strconv.Atoi(m[0:idx]) 95 return fmt.Sprintf("%d-pre.0", n+1) 96 }) 97 98 vv, ve := version.NewVersion(v) 99 vw, we := version.NewVersion(w) 100 101 return ve == nil && we == nil && vv.GreaterThan(vw) 102 }