From d64eb8f2b7fcd69ab6c87be668c3d349b83b7e6c Mon Sep 17 00:00:00 2001 From: Sung Won Cho Date: Fri, 7 Apr 2017 11:44:54 +1000 Subject: [PATCH] Stop heartbeat and autoudpate periodically --- main.go | 52 +++------------- upgrade/upgrade.go | 144 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 152 insertions(+), 44 deletions(-) create mode 100644 upgrade/upgrade.go diff --git a/main.go b/main.go index 9265eeee..5101c9ab 100644 --- a/main.go +++ b/main.go @@ -1,14 +1,15 @@ package main import ( - "encoding/json" "fmt" - "gopkg.in/yaml.v2" "io/ioutil" - "net/http" "os" "os/user" "sort" + + "github.com/dnote-io/cli/upgrade" + + "gopkg.in/yaml.v2" ) type Config struct { @@ -19,7 +20,6 @@ type Note map[string][]string const configFilename = ".dnoterc" const dnoteFilename = ".dnote" -const version = "v0.0.1" func getConfigPath() (string, error) { usr, err := user.Current() @@ -236,49 +236,13 @@ func checkFileExists(filepath string) bool { return !os.IsNotExist(err) } -func checkUpdates() error { - endpoint := "https://api.github.com/repos/dnote-io/cli/releases" - resp, err := http.Get(endpoint) - if err != nil { - return err - } - - defer resp.Body.Close() - - var x []map[string]interface{} - err = json.NewDecoder(resp.Body).Decode(&x) - if err != nil { - return err - } - - if len(x) == 0 { - return nil - } - - latestRelease := x[0] - latestVersion := latestRelease["tag_name"] - - if version != latestVersion { - fmt.Println("==") - fmt.Printf("Update available: %s\n", latestVersion) - fmt.Printf("See: %s\n", x[0]["html_url"]) - fmt.Println("==\n") - } - - return nil -} - -func heartbeat() { - http.Get("http://api.dnote.io/heartbeat") -} - func main() { err := initDnote() check(err) - err = checkUpdates() - check(err) - - heartbeat() + err = upgrade.AutoUpdate() + if err != nil { + fmt.Println("Warning: Failed to check for update", err) + } if len(os.Args) < 2 { fmt.Println("Dnote - Spontaneously capture new engineering lessons\n") diff --git a/upgrade/upgrade.go b/upgrade/upgrade.go new file mode 100644 index 00000000..ae6c9701 --- /dev/null +++ b/upgrade/upgrade.go @@ -0,0 +1,144 @@ +package upgrade + +import ( + "bytes" + "context" + "encoding/binary" + "fmt" + "io" + "io/ioutil" + "net/http" + "os" + "os/exec" + "os/user" + "path" + "runtime" + "time" + + "github.com/google/go-github/github" +) + +const dnoteUpdateFilename = ".dnote-update" +const version = "0.0.3" + +// getAsset finds the asset to download from the liast of assets in a release +func getAsset(release *github.RepositoryRelease) *github.ReleaseAsset { + filename := fmt.Sprintf("dnote-%s-%s", runtime.GOOS, runtime.GOARCH) + + for _, asset := range release.Assets { + if *asset.Name == filename { + return &asset + } + } + + return nil +} +func getDnoteUpdatePath() (string, error) { + usr, err := user.Current() + if err != nil { + return "", err + } + + return fmt.Sprintf("%s/%s", usr.HomeDir, dnoteUpdateFilename), nil +} + +// shouldCheckUpdate checks if update should be checked +func shouldCheckUpdate() (bool, error) { + updatePath, err := getDnoteUpdatePath() + if err != nil { + return false, err + } + + b, err := ioutil.ReadFile(updatePath) + if err != nil { + return false, err + } + + buf := bytes.NewBuffer(b) + lastEpoch, err := binary.ReadVarint(buf) + if err != nil { + return false, err + } + + now := time.Now().Unix() + var epochTarget int64 = 86400 * 7 // 7 days + + return now-lastEpoch > epochTarget, nil +} + +// AutoUpdate triggers update if needed +func AutoUpdate() error { + shouldCheck, err := shouldCheckUpdate() + if err != nil { + return err + } + + if shouldCheck { + Update() + } + + return nil +} + +func Update() error { + // Fetch the latest version + gh := github.NewClient(nil) + releases, _, err := gh.Repositories.ListReleases(context.Background(), "dnote-io", "cli", nil) + + if err != nil { + return err + } + + latest := releases[0] + latestVersion := (*latest.TagName)[1:] + + if err != nil { + return err + } + + // Check if up to date + if latestVersion == version { + fmt.Printf("Up-to-date: %s", version) + return nil + } + + asset := getAsset(latest) + if asset == nil { + fmt.Printf("Could not find the release for %s %s", runtime.GOOS, runtime.GOARCH) + return nil + } + + // Download temporary file + tmpPath := path.Join(os.TempDir(), "dnote_update") + out, err := os.Create(tmpPath) + if err != nil { + return err + } + defer out.Close() + + resp, err := http.Get(*asset.BrowserDownloadURL) + if err != nil { + return err + } + defer resp.Body.Close() + + _, err = io.Copy(out, resp.Body) + if err != nil { + return err + } + + // Override the binary + cmdPath, err := exec.LookPath("dnote") + if err != nil { + return err + } + + err = os.Rename(tmpPath, cmdPath) + if err != nil { + return err + } + + fmt.Printf("Updated: v%s -> v%s", version, latestVersion) + fmt.Println("Change note: https://github.com/dnote-io/cli/releases") + return nil +}