diff --git a/gbm-cli/cmd/release/status.go b/gbm-cli/cmd/release/status.go index 2f1ef95..a695b7d 100644 --- a/gbm-cli/cmd/release/status.go +++ b/gbm-cli/cmd/release/status.go @@ -2,6 +2,7 @@ package release import ( "fmt" + "reflect" "time" "github.com/fatih/color" @@ -39,7 +40,7 @@ var StatusCmd = &cobra.Command{ // Check to see if the release has been published rel, _ := release.GetGbmRelease(version) - if (rel != gh.Release{}) { + if (!reflect.DeepEqual(rel, gh.Release{})) { console.Print(heading, "\nšŸŽ‰ Release %s has been published!\n %s\n", version, rel.Url) } diff --git a/gbm-cli/cmd/root.go b/gbm-cli/cmd/root.go index 97b7b9c..e921336 100644 --- a/gbm-cli/cmd/root.go +++ b/gbm-cli/cmd/root.go @@ -4,8 +4,8 @@ import ( "github.com/spf13/cobra" "github.com/wordpress-mobile/release-toolkit-gutenberg-mobile/gbm-cli/cmd/release" "github.com/wordpress-mobile/release-toolkit-gutenberg-mobile/gbm-cli/cmd/render" + "github.com/wordpress-mobile/release-toolkit-gutenberg-mobile/gbm-cli/cmd/utils" "github.com/wordpress-mobile/release-toolkit-gutenberg-mobile/gbm-cli/pkg/console" - "github.com/wordpress-mobile/release-toolkit-gutenberg-mobile/gbm-cli/pkg/gh" ) const Version = "v1.5.0" @@ -20,20 +20,13 @@ var rootCmd = &cobra.Command{ func Execute() { err := rootCmd.Execute() console.ExitIfError(err) - } func init() { // Add the render command rootCmd.AddCommand(render.RenderCmd) rootCmd.AddCommand(release.ReleaseCmd) - - // Check to see if the user is running the latest version - // of the CLI. If not, let them know. - latestRelease, err := gh.GetLatestRelease("release-toolkit-gutenberg-mobile") - console.ExitIfError(err) - - if latestRelease.TagName != Version { - console.Warn("You are running an older version of the CLI. Please update to %s", latestRelease.TagName) + if !utils.CheckIfTempRun() { + utils.CheckExeVersion(Version) } } diff --git a/gbm-cli/cmd/utils/utils.go b/gbm-cli/cmd/utils/utils.go index 0db3cd5..ec3d0e2 100644 --- a/gbm-cli/cmd/utils/utils.go +++ b/gbm-cli/cmd/utils/utils.go @@ -2,9 +2,15 @@ package utils import ( "fmt" + "net/http" "os" + "os/exec" + "path/filepath" + "strings" + "github.com/inconshreveable/go-update" "github.com/wordpress-mobile/release-toolkit-gutenberg-mobile/gbm-cli/pkg/console" + "github.com/wordpress-mobile/release-toolkit-gutenberg-mobile/gbm-cli/pkg/gh" "github.com/wordpress-mobile/release-toolkit-gutenberg-mobile/gbm-cli/pkg/semver" ) @@ -35,3 +41,73 @@ func Exit(code int, deferred ...func()) { return code }()) } + +// Checks if running from a temp directory (go build) +// Useful for checking if running via `go run main.go` +// We ignore errors since this only relevant to local development +func CheckIfTempRun() bool { + ex, _ := os.Executable() + dir := filepath.Dir(ex) + return strings.Contains(dir, "go-build") +} + +// Updates the currently running executable. +func UpdateExe(url string) error { + resp, err := http.Get(url) + if err != nil { + return err + } + defer resp.Body.Close() + if err := update.Apply(resp.Body, update.Options{}); err != nil { + return err + } + + return nil +} + +// Gets the download url for the gbm-cli executable +func exeDownloadUrl(release gh.Release) string { + for _, asset := range release.Assets { + if strings.Contains(asset.Name, "gbm-cli") { + return asset.DownloadUrl + } + } + return "" +} + +// Checks if the currently running executable is the latest version +// If not, prompts the user to update. +// If update is confirmed, the executable is updated and the process is restarted +func CheckExeVersion(version string) { + latestRelease, err := gh.GetLatestRelease("release-toolkit-gutenberg-mobile") + console.ExitIfError(err) + + if latestRelease.TagName != version { + if console.Confirm("You are running an older version of the CLI. Would you like to update?") { + + if url := exeDownloadUrl(latestRelease); url != "" { + if err := UpdateExe(url); err != nil { + console.ExitError("Could not update the CLI: %v", err) + } else { + console.Info("CLI updated successfully") + reStart() + } + } else { + console.ExitError("Could not find download url for latest release") + } + } + } +} + +// Restarts the process +func reStart() { + args := os.Args + cmd := exec.Command(args[0], args[1:]...) + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + cmd.Stdin = os.Stdin + if err := cmd.Run(); err != nil { + os.Exit(1) + } + os.Exit(0) +} diff --git a/gbm-cli/go.mod b/gbm-cli/go.mod index 65eecb9..07f1d32 100644 --- a/gbm-cli/go.mod +++ b/gbm-cli/go.mod @@ -7,6 +7,7 @@ toolchain go1.21.2 require ( github.com/davecgh/go-spew v1.1.1 github.com/fatih/color v1.15.0 + github.com/inconshreveable/go-update v0.0.0-20160112193335-8152e7eb6ccf github.com/kylelemons/godebug v1.1.0 github.com/mikefarah/yq/v4 v4.35.2 github.com/spf13/cobra v1.7.0 diff --git a/gbm-cli/go.sum b/gbm-cli/go.sum index c66bef9..f0b19e7 100644 --- a/gbm-cli/go.sum +++ b/gbm-cli/go.sum @@ -35,6 +35,8 @@ github.com/henvic/httpretty v0.1.2 h1:EQo556sO0xeXAjP10eB+BZARMuvkdGqtfeS4Ntjvki github.com/henvic/httpretty v0.1.2/go.mod h1:ViEsly7wgdugYtymX54pYp6Vv2wqZmNHayJ6q8tlKCc= github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM= github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg= +github.com/inconshreveable/go-update v0.0.0-20160112193335-8152e7eb6ccf h1:WfD7VjIE6z8dIvMsI4/s+1qr5EL+zoIGev1BQj1eoJ8= +github.com/inconshreveable/go-update v0.0.0-20160112193335-8152e7eb6ccf/go.mod h1:hyb9oH7vZsitZCiBt0ZvifOrB+qc8PS5IiilCIb87rg= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/jinzhu/copier v0.4.0 h1:w3ciUoD19shMCRargcpm0cm91ytaBhDvuRpz1ODO/U8= diff --git a/gbm-cli/pkg/gh/gh.go b/gbm-cli/pkg/gh/gh.go index 31f80eb..0701776 100644 --- a/gbm-cli/pkg/gh/gh.go +++ b/gbm-cli/pkg/gh/gh.go @@ -102,6 +102,15 @@ type Release struct { Prerelease bool Target string `json:"target_commitish"` PublishedAt string `json:"published_at"` + Checksum string + Assets []ReleaseAsset +} + +type ReleaseAsset struct { + Name string + ContentType string `json:"content_type"` + Size int + DownloadUrl string `json:"browser_download_url"` } type Commit struct {