Skip to content

Commit 2a10f4e

Browse files
authored
Merge pull request #17 from ReconfigureIO/feature/self-update
Add update command to let reco self-update
2 parents 8fe77fb + d8f2e36 commit 2a10f4e

File tree

6 files changed

+182
-5
lines changed

6 files changed

+182
-5
lines changed

Makefile

+2-2
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ LDFLAGS := -X 'main.version=$(VERSION)' \
1717
-X 'main.buildTime=$(BUILDTIME)' \
1818
-X 'main.builder=$(BUILDER)' \
1919
-X 'main.goversion=$(GOVERSION)' \
20+
-X 'main.target=$(TARGET)' \
2021
-X 'github.com/ReconfigureIO/reco.alternativePlatformServer=$(API_SERVER)'
2122
CODEBUILD_NAME := "sample-snap-builder"
2223
GO_EXTENSION :=
@@ -77,8 +78,7 @@ dist/%-${VERSION}-${TARGET}.zip: build/${TARGET}/%${GO_EXTENSION} | dist
7778
packages: $(PKG_TARGETS)
7879

7980
install: $(TARGETS)
80-
cp ${TARGETS} /usr/local/bin
81-
81+
cp ${TARGETS} /go/bin
8282
clean:
8383
rm -rf ./dist $(TARGETS) ./build
8484

cmd/reco/main.go

+2
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ func main() {
88
cmd.BuildInfo.BuildTime = buildTime
99
cmd.BuildInfo.Builder = builder
1010
cmd.BuildInfo.GoVersion = goversion
11+
cmd.BuildInfo.Target = target
1112

1213
// execute
1314
cmd.Execute()
@@ -18,4 +19,5 @@ var (
1819
buildTime string
1920
builder string
2021
goversion string
22+
target string
2123
)

cmd/update.go

+143
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
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+
}

cmd/update_test.go

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package cmd
2+
3+
import (
4+
"fmt"
5+
"testing"
6+
7+
"github.com/google/go-github/github"
8+
)
9+
10+
func TestLatestRelease(t *testing.T) {
11+
latest, err := latestRelease(github.NewClient(nil))
12+
if err != nil {
13+
t.Error(err)
14+
}
15+
if latest == "" {
16+
t.Error("Returned string is empty")
17+
}
18+
fmt.Println(latest)
19+
}

cmd/version.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
package cmd
22

33
import (
4-
"github.com/ReconfigureIO/reco/logger"
54
"github.com/ReconfigureIO/cobra"
5+
"github.com/ReconfigureIO/reco/logger"
66
)
77

88
// BuildInfo is the build information of reco binary. This is
99
// set at build time by ldflags.
1010
var BuildInfo struct {
11-
Version, BuildTime, Builder, GoVersion string
11+
Version, BuildTime, Builder, GoVersion, Target string
1212
}
1313

1414
// versionCmd represents the version command

glide.lock

+14-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)