Skip to content
This repository was archived by the owner on Jan 16, 2021. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 8 additions & 38 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import (
"io"
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm

"net/url"
"os"
"os/exec"
"os/signal"
"path/filepath"
"runtime"
Expand All @@ -26,10 +25,7 @@ const (
defaultBaseURL = "https://api.parse.com/1/"
)

var (
autoUpdate = false
userAgent = fmt.Sprintf("parse-cli-%s-%s", runtime.GOOS, version)
)
var userAgent = fmt.Sprintf("parse-cli-%s-%s", runtime.GOOS, version)

type versionCmd struct{}

Expand Down Expand Up @@ -377,39 +373,13 @@ func main() {
}
e.Client = client

// autoUpdate is false for all non-production builds
// so we never auto update
// for production builds autoUpdate is true but
// we suppress auto-update iff PARSE_NOUPDATE is not set
if autoUpdate && os.Getenv("PARSE_NOUPDATE") == "" {
// Perform a best effort update
updated, err := (&updateCmd{}).updateCLI(&e)
if err != nil {
cmd := exec.Command(os.Args[0], "version")
if err := cmd.Run(); err != nil {
fmt.Fprintf(e.Out, `parse cli corrupted during update.
Please follow instructions at:
https://parse.com/apps/quickstart#cloud_code
to install a new cli.`)
os.Exit(1)
}
}

if updated {
// Re-run the command with the updated CLI
cmd := exec.Cmd{
Path: os.Args[0],
Args: os.Args,
Stdin: os.Stdin,
Stdout: os.Stdout,
Stderr: os.Stderr,
}

if err := cmd.Run(); err != nil {
os.Exit(1)
}
return
}
message, err := checkIfSupported(&e, version)
if err != nil {
fmt.Fprintln(e.Err, err)
os.Exit(1)
}
if message != "" {
fmt.Fprintln(e.Err, message)
}

if err := rootCmd(&e).Execute(); err != nil {
Expand Down
62 changes: 35 additions & 27 deletions update_cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,7 @@ import (
"fmt"
"net/http"
"net/url"
"path"
"runtime"
"strings"

"github.com/facebookgo/stackerr"
"github.com/inconshreveable/go-update"
Expand All @@ -15,50 +13,60 @@ import (
)

const (
macCliDownloadURL = "https://parse.com/downloads/cloud_code/cli/parse-osx/latest"
unixCliDownloadURL = "https://parse.com/downloads/cloud_code/cli/parse-linux/latest"
windowsCliDownloadURL = "https://parse.com/downloads/cloud_code/cli/parse-windows/latest"
macDownload = "parse"
windowsDownload = "parse.exe"
linuxDownload = "parse_linux"
linuxArmDownload = "parse_linux_arm"
downloadURLFormat = "https://github.com/ParsePlatform/parse-cli/releases/download/release_%s/%s"
)

type updateCmd struct{}

func (u *updateCmd) latestVersion(e *env, downloadURL string) (string, error) {
dURL, err := url.Parse(downloadURL)
if err != nil {
return "", stackerr.Wrap(err)
func (u *updateCmd) latestVersion(e *env) (string, error) {
v := make(url.Values)
v.Set("version", "latest")
req := &http.Request{
Method: "GET",
URL: &url.URL{Path: "supported", RawQuery: v.Encode()},
}

resp, err := e.Client.Do(&http.Request{Method: "HEAD", URL: dURL}, nil, nil)
if err != nil {
return "", nil // if unable to fetch latest cli version, do not abort!
var res struct {
Version string `json:"version"`
}

base := path.Base(resp.Header.Get("Location"))
base = strings.TrimSuffix(base, ".exe")
//parse-os-2.0.2
splits := strings.Split(base, "-")
if _, err := e.Client.Do(req, nil, &res); err != nil {
return "", stackerr.Wrap(err)
}

return splits[len(splits)-1], nil
return res.Version, nil
}

func (u *updateCmd) updateCLI(e *env) (bool, error) {
downloadURL := unixCliDownloadURL
switch runtime.GOOS {
case "windows":
downloadURL = windowsCliDownloadURL
case "darwin":
downloadURL = macCliDownloadURL
}
ostype := runtime.GOOS
arch := runtime.GOARCH

latestVersion, err := u.latestVersion(e, downloadURL)
latestVersion, err := u.latestVersion(e)
if err != nil {
return false, stackerr.Wrap(err)
return false, err
}

if latestVersion == "" || latestVersion == version {
return false, nil
}

var downloadURL string
switch ostype {
case "darwin":
downloadURL = fmt.Sprintf(downloadURLFormat, latestVersion, macDownload)
case "windows":
downloadURL = fmt.Sprintf(downloadURLFormat, latestVersion, windowsDownload)
case "linux":
if arch == "arm" {
downloadURL = fmt.Sprintf(downloadURLFormat, latestVersion, linuxArmDownload)
} else {
downloadURL = fmt.Sprintf(downloadURLFormat, latestVersion, linuxDownload)
}
}

exec, err := osext.Executable()
if err != nil {
return false, stackerr.Wrap(err)
Expand Down
39 changes: 11 additions & 28 deletions update_cmd_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@ package main
import (
"io/ioutil"
"net/http"
"strings"
"testing"

"github.com/facebookgo/ensure"
"github.com/facebookgo/jsonpipe"
"github.com/facebookgo/parse"
)

Expand All @@ -17,37 +17,20 @@ func TestLatestVersion(t *testing.T) {
defer h.Stop()

ht := transportFunc(func(r *http.Request) (*http.Response, error) {
var result string
switch r.URL.String() {
case windowsCliDownloadURL:
result = "http://parse-cli.aws.com/hash/parse-windows-2.0.2.exe"
case unixCliDownloadURL:
result = "http://parse-cli.aws.com/hash/parse-linux-2.0.2"
case macCliDownloadURL:
result = "http://parse-cli.aws.com/hash/parse-osx-2.0.2"
}
resp := http.Response{
ensure.DeepEqual(t, r.URL.Path, "/1/supported")
return &http.Response{
StatusCode: http.StatusOK,
Body: ioutil.NopCloser(strings.NewReader("Success!")),
}
if resp.Header == nil {
resp.Header = make(http.Header)
}
resp.Header.Set("Location", result)
return &resp, nil
Body: ioutil.NopCloser(
jsonpipe.Encode(
map[string]string{"version": "2.0.2"},
),
),
}, nil
})
h.env.Client = &Client{client: &parse.Client{Transport: ht}}
u := new(updateCmd)

version, err := u.latestVersion(h.env, unixCliDownloadURL)
latestVersion, err := u.latestVersion(h.env)
ensure.Nil(t, err)
ensure.DeepEqual(t, version, "2.0.2")

version, err = u.latestVersion(h.env, macCliDownloadURL)
ensure.Nil(t, err)
ensure.DeepEqual(t, version, "2.0.2")

version, err = u.latestVersion(h.env, windowsCliDownloadURL)
ensure.Nil(t, err)
ensure.DeepEqual(t, version, "2.0.2")
ensure.DeepEqual(t, latestVersion, "2.0.2")
}
19 changes: 19 additions & 0 deletions utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package main
import (
"errors"
"fmt"
"net/http"
"net/url"
"regexp"
"strconv"
Expand Down Expand Up @@ -104,3 +105,21 @@ func getHostFromURL(urlStr string) (string, error) {
}
return server, nil
}

func checkIfSupported(e *env, version string) (string, error) {
v := make(url.Values)
v.Set("version", version)
req := &http.Request{
Method: "GET",
URL: &url.URL{Path: "supported", RawQuery: v.Encode()},
}

var res struct {
Warning string `json:"warning"`
}

if _, err := e.Client.Do(req, nil, &res); err != nil {
return "", stackerr.Wrap(err)
}
return res.Warning, nil
}
49 changes: 49 additions & 0 deletions utils_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,14 @@ package main

import (
"crypto/rand"
"io/ioutil"
"net/http"
"regexp"
"testing"

"github.com/facebookgo/ensure"
"github.com/facebookgo/jsonpipe"
"github.com/facebookgo/parse"
)

func TestBothEmpty(t *testing.T) {
Expand Down Expand Up @@ -83,3 +87,48 @@ func TestGetHostFromURL(t *testing.T) {
host, err = getHostFromURL(urlStr)
ensure.Err(t, err, regexp.MustCompile("not a valid url"))
}

func TestIsSupportedWarning(t *testing.T) {
t.Parallel()

h := newHarness(t)
defer h.Stop()

ht := transportFunc(func(r *http.Request) (*http.Response, error) {
ensure.DeepEqual(t, r.URL.Path, "/1/supported")
return &http.Response{
StatusCode: http.StatusOK,
Body: ioutil.NopCloser(
jsonpipe.Encode(
map[string]string{"warning": "please update"},
),
),
}, nil
})
h.env.Client = &Client{client: &parse.Client{Transport: ht}}
message, err := checkIfSupported(h.env, "2.0.2")
ensure.Nil(t, err)
ensure.DeepEqual(t, message, "please update")
}

func TestIsSupportedError(t *testing.T) {
t.Parallel()

h := newHarness(t)
defer h.Stop()

ht := transportFunc(func(r *http.Request) (*http.Response, error) {
ensure.DeepEqual(t, r.URL.Path, "/1/supported")
return &http.Response{
StatusCode: http.StatusBadRequest,
Body: ioutil.NopCloser(
jsonpipe.Encode(
map[string]string{"error": "not supported"},
),
),
}, nil
})
h.env.Client = &Client{client: &parse.Client{Transport: ht}}
_, err := checkIfSupported(h.env, "2.0.2")
ensure.Err(t, err, regexp.MustCompile("not supported"))
}