|
| 1 | +package cmd |
| 2 | + |
| 3 | +import ( |
| 4 | + "archive/zip" |
| 5 | + "bytes" |
| 6 | + "context" |
| 7 | + "fmt" |
| 8 | + "io/ioutil" |
| 9 | + "log" |
| 10 | + "net/http" |
| 11 | + |
| 12 | + "github.com/ReconfigureIO/cobra" |
| 13 | + "github.com/ReconfigureIO/go-update" |
| 14 | + "github.com/ReconfigureIO/reco/logger" |
| 15 | + "github.com/google/go-github/github" |
| 16 | +) |
| 17 | + |
| 18 | +const ( |
| 19 | + recoDownloadAddress = "https://s3.amazonaws.com/reconfigure.io/reco/releases/" |
| 20 | +) |
| 21 | + |
| 22 | +// updateCmd represents the update command |
| 23 | +var ( |
| 24 | + updateCmd = &cobra.Command{ |
| 25 | + Use: "update", |
| 26 | + Short: "Update reco to the latest version", |
| 27 | + Run: updateHandler, |
| 28 | + } |
| 29 | + |
| 30 | + justDoIt bool |
| 31 | +) |
| 32 | + |
| 33 | +func init() { |
| 34 | + RootCmd.AddCommand(updateCmd) |
| 35 | + updateCmd.PersistentFlags().BoolVar(&justDoIt, "just-do-it", justDoIt, "Download and apply update without user interaction") |
| 36 | +} |
| 37 | + |
| 38 | +func updateHandler(cmd *cobra.Command, args []string) { |
| 39 | + if BuildInfo.Version == "" { |
| 40 | + logger.Std.Println("reco version: untracked dev build") |
| 41 | + logger.Std.Println("Cannot automatically update from this version") |
| 42 | + return |
| 43 | + } |
| 44 | + logger.Std.Println("You are using reco ", BuildInfo.Version) |
| 45 | + latest, err := latestRelease(github.NewClient(nil)) |
| 46 | + if err != nil { |
| 47 | + logger.Std.Println("Could not retrieve latest verion info from Github: ", err) |
| 48 | + return |
| 49 | + } else { |
| 50 | + logger.Std.Println("The latest release is reco ", latest) |
| 51 | + } |
| 52 | + |
| 53 | + if justDoIt { |
| 54 | + err = UpgradeTo(latest, BuildInfo.Target) |
| 55 | + if err != nil { |
| 56 | + exitWithError(err) |
| 57 | + } else { |
| 58 | + return |
| 59 | + } |
| 60 | + } |
| 61 | + |
| 62 | + if latest != BuildInfo.Version { |
| 63 | + logger.Std.Println("Would you like to upgrade? (Y/N)") |
| 64 | + upgrade := askForConfirmation() |
| 65 | + if upgrade == true { |
| 66 | + if err := UpgradeTo(latest, BuildInfo.Target); err != nil { |
| 67 | + exitWithError(err) |
| 68 | + } |
| 69 | + } |
| 70 | + |
| 71 | + } else { |
| 72 | + logger.Std.Println("You are using the latest version") |
| 73 | + return |
| 74 | + } |
| 75 | + |
| 76 | + return |
| 77 | +} |
| 78 | + |
| 79 | +// latestRelease gets the version number of the latest reco release |
| 80 | +func latestRelease(client *github.Client) (string, error) { |
| 81 | + release, _, err := client.Repositories.GetLatestRelease(context.Background(), "ReconfigureIO", "reco") |
| 82 | + if err != nil { |
| 83 | + return "", err |
| 84 | + } |
| 85 | + return *release.TagName, nil |
| 86 | +} |
| 87 | + |
| 88 | +func askForConfirmation() bool { |
| 89 | + var response string |
| 90 | + _, err := fmt.Scanln(&response) |
| 91 | + if err != nil { |
| 92 | + log.Fatal(err) |
| 93 | + } |
| 94 | + okayResponses := []string{"y", "Y", "yes", "Yes", "YES"} |
| 95 | + nokayResponses := []string{"n", "N", "no", "No", "NO"} |
| 96 | + if containsString(okayResponses, response) { |
| 97 | + return true |
| 98 | + } else if containsString(nokayResponses, response) { |
| 99 | + return false |
| 100 | + } else { |
| 101 | + fmt.Println("Please type yes or no and then press enter:") |
| 102 | + return askForConfirmation() |
| 103 | + } |
| 104 | +} |
| 105 | + |
| 106 | +func posString(slice []string, element string) int { |
| 107 | + for index, elem := range slice { |
| 108 | + if elem == element { |
| 109 | + return index |
| 110 | + } |
| 111 | + } |
| 112 | + return -1 |
| 113 | +} |
| 114 | + |
| 115 | +// containsString returns true iff slice contains element |
| 116 | +func containsString(slice []string, element string) bool { |
| 117 | + return !(posString(slice, element) == -1) |
| 118 | +} |
| 119 | + |
| 120 | +func UpgradeTo(version string, platform string) error { |
| 121 | + downloadURL := recoDownloadAddress + "reco-" + version + "-" + platform + ".zip" |
| 122 | + resp, err := http.Get(downloadURL) |
| 123 | + if err != nil { |
| 124 | + return err |
| 125 | + } |
| 126 | + defer resp.Body.Close() |
| 127 | + zipFile, err := ioutil.ReadAll(resp.Body) |
| 128 | + if err != nil { |
| 129 | + return err |
| 130 | + } |
| 131 | + unzip, err := zip.NewReader(bytes.NewReader(zipFile), int64(len(zipFile))) |
| 132 | + if err != nil { |
| 133 | + return err |
| 134 | + } |
| 135 | + newReco, err := unzip.File[0].Open() |
| 136 | + err = update.Apply(newReco, update.Options{}) |
| 137 | + if err != nil { |
| 138 | + logger.Std.Println("Error occured during self-update") |
| 139 | + return err |
| 140 | + } |
| 141 | + logger.Std.Println("Self-update successful") |
| 142 | + return err |
| 143 | +} |
0 commit comments