cli: add version flag in update-cli command (#3996)

Co-authored-by: Shahidh K Muhammed <muhammedshahid.k@gmail.com>
This commit is contained in:
Shahidh K Muhammed 2020-03-05 12:25:33 +05:30 committed by GitHub
parent cb75660b3e
commit d7d53f222c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 74 additions and 40 deletions

View File

@ -5,6 +5,8 @@ import (
"os"
"strings"
"github.com/Masterminds/semver"
"github.com/hasura/graphql-engine/cli"
"github.com/hasura/graphql-engine/cli/update"
"github.com/pkg/errors"
@ -20,6 +22,9 @@ const updateCLICmdExample = ` # Update CLI to latest version:
# To disable auto-update check on the CLI, set
# "show_update_notification": false
# in ~/.hasura/config.json
# Update CLI to a specific version (say v1.2.0-beta.1):
hasura update-cli --version v1.2.0-beta.1
`
// NewUpdateCLICmd returns the update-cli command.
@ -29,7 +34,7 @@ func NewUpdateCLICmd(ec *cli.ExecutionContext) *cobra.Command {
}
updateCmd := &cobra.Command{
Use: updateCLICmdUse,
Short: "Update the CLI to latest version",
Short: "Update the CLI to latest or a specific version",
SilenceUsage: true,
PreRunE: func(cmd *cobra.Command, args []string) error {
return ec.Prepare()
@ -39,43 +44,77 @@ func NewUpdateCLICmd(ec *cli.ExecutionContext) *cobra.Command {
},
Example: updateCLICmdExample,
}
f := updateCmd.Flags()
f.StringVar(&opts.version, "version", "", "a specific version to install")
return updateCmd
}
type updateOptions struct {
EC *cli.ExecutionContext
version string
}
func (o *updateOptions) run(showPrompt bool) error {
func (o *updateOptions) run(showPrompt bool) (err error) {
currentVersion := o.EC.Version.CLISemver
if currentVersion == nil {
return errors.Errorf("cannot update from a non-semver version: %s", o.EC.Version.GetCLIVersion())
}
o.EC.Spin("Checking for update... ")
hasUpdate, latestVersion, err := update.HasUpdate(currentVersion, o.EC.LastUpdateCheckFile)
o.EC.Spinner.Stop()
if err != nil {
return errors.Wrap(err, "command: check update")
var versionToBeInstalled *semver.Version
if o.version != "" {
// parse the version
versionToBeInstalled, err = semver.NewVersion(o.version)
if err != nil {
return errors.Wrap(err, "unable to parse version")
}
} else {
o.EC.Spin("Checking for update... ")
hasUpdate, latestVersion, hasPreReleaseUpdate, preReleaseVersion, err := update.HasUpdate(currentVersion, o.EC.LastUpdateCheckFile)
o.EC.Spinner.Stop()
if err != nil {
return errors.Wrap(err, "command: check update")
}
ec.Logger.Debugln("hasUpdate: ", hasUpdate, "latestVersion: ", latestVersion, "hasPreReleaseUpdate: ", hasPreReleaseUpdate, "preReleaseVersion: ", preReleaseVersion, "currentVersion:", currentVersion)
if showPrompt {
switch {
case hasUpdate:
ok := ask2confirm(latestVersion.String(), o.EC.Logger)
if !ok {
o.EC.Logger.Info("skipping update, run 'hasura update-cli' to update manually")
return nil
}
versionToBeInstalled = latestVersion
case hasPreReleaseUpdate:
o.EC.Logger.Infof(`a new pre-release version is available:
- %s (changelog: %s)
to update cli to this version, execute:
hasura update-cli --version %s
`, preReleaseVersion.Original(), getChangeLogLink(preReleaseVersion), preReleaseVersion.Original())
return nil
}
} else {
if hasUpdate {
versionToBeInstalled = latestVersion
}
}
}
ec.Logger.Debugln("hasUpdate: ", hasUpdate, "latestVersion: ", latestVersion, "currentVersion:", currentVersion)
if !hasUpdate {
if versionToBeInstalled == nil {
o.EC.Logger.WithField("version", currentVersion).Info("hasura cli is up to date")
return nil
}
if showPrompt {
ok := ask2confirm(latestVersion.String(), o.EC.Logger)
if !ok {
o.EC.Logger.Info("skipping update, run 'hasura update-cli' to update manually")
return nil
}
}
ec.Logger.Debugln("versionToBeInstalled: ", versionToBeInstalled.String())
o.EC.Spin(fmt.Sprintf("Updating cli to v%s... ", latestVersion.String()))
err = update.ApplyUpdate(latestVersion)
o.EC.Spin(fmt.Sprintf("Updating cli to v%s... ", versionToBeInstalled.String()))
err = update.ApplyUpdate(versionToBeInstalled)
o.EC.Spinner.Stop()
if err != nil {
if os.IsPermission(err) {
@ -84,7 +123,7 @@ func (o *updateOptions) run(showPrompt bool) error {
return errors.Wrap(err, "apply update")
}
o.EC.Logger.WithField("version", "v"+latestVersion.String()).Info("Updated to latest version")
o.EC.Logger.WithField("version", "v"+versionToBeInstalled.String()).Info("Updated to latest version")
return nil
}
@ -106,3 +145,7 @@ func ask2confirm(v string, log *logrus.Logger) bool {
}
return false
}
func getChangeLogLink(version *semver.Version) string {
return fmt.Sprintf("https://github.com/hasura/graphql-engine/releases/tag/%s", version.Original())
}

View File

@ -22,28 +22,24 @@ import (
const updateCheckURL = "https://releases.hasura.io/graphql-engine?agent=cli"
type updateCheckResponse struct {
Latest string `json:"latest"`
Latest *semver.Version `json:"latest"`
PreRelease *semver.Version `json:"prerelease"`
}
func getLatestVersion() (*semver.Version, error) {
func getLatestVersion() (*semver.Version, *semver.Version, error) {
res, err := http.Get(updateCheckURL)
if err != nil {
return nil, errors.Wrap(err, "update check request")
return nil, nil, errors.Wrap(err, "update check request")
}
defer res.Body.Close()
var response updateCheckResponse
err = json.NewDecoder(res.Body).Decode(&response)
if err != nil {
return nil, errors.Wrap(err, "decoding update check response")
return nil, nil, errors.Wrap(err, "decoding update check response")
}
v, err := semver.NewVersion(response.Latest)
if err != nil {
return nil, errors.Wrap(err, "semver parsing")
}
return v, nil
return response.Latest, response.PreRelease, nil
}
func buildAssetURL(v string) string {
@ -88,23 +84,18 @@ func downloadAsset(url, fileName, filePath string) (*os.File, error) {
return asset, nil
}
// HasUpdate tells us if there is a new update available.
func HasUpdate(currentVersion *semver.Version, timeFile string) (bool, *semver.Version, error) {
// HasUpdate tells us if there is a new stable or prerelease update available.
func HasUpdate(currentVersion *semver.Version, timeFile string) (bool, *semver.Version, bool, *semver.Version, error) {
if timeFile != "" {
defer writeTimeToFile(timeFile, time.Now().UTC())
}
latestVersion, err := getLatestVersion()
latestVersion, preReleaseVersion, err := getLatestVersion()
if err != nil {
return false, nil, errors.Wrap(err, "get latest version")
return false, nil, false, nil, errors.Wrap(err, "get latest version")
}
c, err := semver.NewConstraint(fmt.Sprintf("> %s", currentVersion.String()))
if err != nil {
return false, nil, errors.Wrap(err, "semver constraint build")
}
return c.Check(latestVersion), latestVersion, nil
return latestVersion.GreaterThan(currentVersion), latestVersion, preReleaseVersion.GreaterThan(currentVersion), preReleaseVersion, nil
}
// ApplyUpdate downloads and applies the update indicated by version v.