Repository: nats-io/nsc
Pull request #292 (2020-09-08 21:30:15)

Removed automatic self update and added option to specify target ver
Signed-off-by: Matthias Hanel <mh@synadia.com>

```
 17:28:37  ~/repos/nsc  noselfupdate *8  go build
 17:28:45  ~/repos/nsc  noselfupdate *8 ?1  nsc update
Downloading version: latest⠸ Successfully updated to version 0.4.10

Release Notes:

## Release Notes

- #282 - Fixed an issue where describing an account that contained 'private'
exports the rendering of the table marked them as being 'public'.



 17:28:51  ~/repos/nsc  noselfupdate *8 ?1  go build
 17:29:03  ~/repos/nsc  noselfupdate *8 ?1  nsc update --version 0.4.9
Error: version 0.4.9 not found
Usage:
  nsc update [flags]

Examples:
nsc update

Flags:
  -h, --help              help for update
      --version version   version to updated the nsc binary to (default latest)

Global Flags:
  -i, --interactive          ask questions for various settings
  -K, --private-key string   private key

 17:29:09  ✘ 1  ~/r/nsc  noselfupdate *8 ?1  nsc update --version 0.4.8
Downloading version: 0.4.8⠸ Successfully updated to version 0.4.8

Release Notes:

## Release Notes

- #240 - Added the environment variables `NSC_CWD_ONLY`, `NSC_NO_SELF_UPDATE`,
and `NSC_NO_GIT_IGNORE` which when set modify standard behaviors in nsc which
may be useful when generating configurations via scripts.

- #275 - Added support for bearer tokens. This functionality requires a
supporting server. It is intended for WebSocket clients that present JWTs that
are valid for a session.

- #274 - Added internal command to rename an account (`nsc rename account`)

- #246 - Fixed the ability to create an account while specifying a public key
already stored in the Keystore. In non-interactive cases, the specified public
key was not resolved to a private key.

- #263 - Added the ability to specify `--account-jwt-server-url` flag on the
push command. This enables pushing a JWT to an account server that is not
advertised by the operator JWT.

- #254 - Fixed online help for documentation the import keys --dir flag.


 17:29:14  ~/repos/nsc  noselfupdate *8 ?1  nsc --version
nsc version 0.4.8
 17:29:43  ~/repos/nsc  noselfupdate *8 ?1  go build
 17:29:48  ~/repos/nsc  noselfupdate *8 ?1  nsc update --version latest
Downloading version: latest⠹ Successfully updated to version 0.4.10

Release Notes:

## Release Notes

- #282 - Fixed an issue where describing an account that contained 'private'
exports the rendering of the table marked them as being 'public'.



 17:30:00  ~/repos/nsc  noselfupdate *8 ?1  nsc --version
nsc version 0.4.10
 17:30:01  ~/repos/nsc  noselfupdate *8 ?1 
```

matthiashanel: > I would separate the dependency update from this feature, and do a separate PR for the dep updates.

Turns out the upgrade was not necessary. Just appeared this way during developement.

Patch:
diff --git a/cmd/defaults.go b/cmd/defaults.go
index 04a0b31..b8669dc 100644
--- a/cmd/defaults.go
+++ b/cmd/defaults.go
@@ -34,7 +34,6 @@ import (
 //NscHomeEnv the folder for the config file
 const NscHomeEnv = "NSC_HOME"
 const NscCwdOnlyEnv = "NSC_CWD_ONLY"
-const NscNoSelfUpdateEnv = "NSC_NO_SELF_UPDATE"
 const NscNoGitIgnoreEnv = "NSC_NO_GIT_IGNORE"
 
 type ToolConfig struct {
diff --git a/cmd/env.go b/cmd/env.go
index e097606..39880a4 100644
--- a/cmd/env.go
+++ b/cmd/env.go
@@ -95,7 +95,6 @@ func (p *SetContextParams) PrintEnv(cmd *cobra.Command) {
 	table.AddHeaders("Setting", "Set", "Effective Value")
 	table.AddRow("$"+NscCwdOnlyEnv, envSet(NscCwdOnlyEnv), "If set, default operator/account from cwd only")
 	table.AddRow("$"+NscNoGitIgnoreEnv, envSet(NscNoGitIgnoreEnv), "If set, no .gitignore files written")
-	table.AddRow("$"+NscNoSelfUpdateEnv, envSet(NscNoSelfUpdateEnv), "If set, no self-update checks")
 	table.AddRow("$"+store.NKeysPathEnv, envSet(store.NKeysPathEnv), AbbrevHomePaths(store.GetKeysDir()))
 	table.AddRow("$"+homeEnv, envSet(homeEnv), AbbrevHomePaths(toolHome))
 	table.AddRow("Config", "", AbbrevHomePaths(conf.configFile()))
diff --git a/cmd/root.go b/cmd/root.go
index 13f80e2..ccc25e7 100644
--- a/cmd/root.go
+++ b/cmd/root.go
@@ -34,7 +34,6 @@ const TestEnv = "NSC_TEST"
 
 var KeyPathFlag string
 var InteractiveFlag bool
-var NscNoSelfUpdate bool
 var NscCwdOnly bool
 var quietMode bool
 
@@ -153,9 +152,6 @@ func SetEnvOptions() {
 	if _, ok := os.LookupEnv(NscNoGitIgnoreEnv); ok {
 		store.NscNotGitIgnore = true
 	}
-	if _, ok := os.LookupEnv(NscNoSelfUpdateEnv); ok {
-		NscNoSelfUpdate = true
-	}
 	if _, ok := os.LookupEnv(NscCwdOnlyEnv); ok {
 		NscCwdOnly = true
 	}
diff --git a/cmd/update.go b/cmd/update.go
index 01bc537..2236179 100644
--- a/cmd/update.go
+++ b/cmd/update.go
@@ -18,16 +18,43 @@ package cmd
 import (
 	"fmt"
 	"os"
+	"strings"
 	"time"
 
 	"github.com/blang/semver"
+
 	"github.com/briandowns/spinner"
 	cli "github.com/nats-io/cliprompts/v2"
 	"github.com/rhysd/go-github-selfupdate/selfupdate"
 	"github.com/spf13/cobra"
 )
 
+type semV string
+
+func (r *semV) String() string {
+	if *r == "" {
+		return "latest"
+	}
+	return string(*r)
+}
+
+func (r *semV) Type() string {
+	return "version"
+}
+
+func (r *semV) Set(s string) error {
+	if strings.ToLower(strings.TrimSpace(s)) == "latest" {
+		*r = ""
+	} else if v, err := semver.ParseTolerant(s); err != nil {
+		return err
+	} else {
+		*r = semV(v.String())
+	}
+	return nil
+}
+
 func createUpdateCommand() *cobra.Command {
+	ver := semV("")
 	var cmd = &cobra.Command{
 		Example: "nsc update",
 		Use:     "update",
@@ -38,50 +65,53 @@ func createUpdateCommand() *cobra.Command {
 			if err != nil {
 				return err
 			}
-
+			cmdPath, err := os.Executable()
+			if err != nil {
+				return err
+			}
 			su, err := NewSelfUpdate()
 			if err != nil {
 				return err
 			}
-			nvs, err := su.doCheck()
+			nvs, err := su.doCheck(string(ver))
 			if err != nil {
 				return err
 			}
 			if nvs == nil {
-				cmd.Println("Current version", v, "is the latest")
+				cmd.Println("Current version", v, "is requested version")
 				return nil
 			}
 
 			wait := spinner.New(spinner.CharSets[14], 250*time.Millisecond)
 			defer wait.Stop()
 
-			wait.Prefix = "Downloading latest version "
+			wait.Prefix = fmt.Sprintf("Downloading version: %s", ver.String())
 			_ = wait.Color("italic")
 			wait.Start()
 
-			var latest *selfupdate.Release
 			if updateFn == nil {
 				// the library freak out if GITHUB_TOKEN is set - don't break travis :)
 				_ = os.Setenv("GITHUB_TOKEN", "")
-				latest, err = selfupdate.UpdateSelf(v, GetConfig().GithubUpdates)
+
+				err = selfupdate.DefaultUpdater().UpdateTo(nvs, cmdPath)
 			} else {
-				latest, err = updateFn(v, GetConfig().GithubUpdates)
+				err = updateFn(nvs, cmdPath)
 			}
 			if err != nil {
 				cmd.SilenceErrors = false
 				return err
 			}
 
-			cmd.Printf("Successfully updated to version %s\n", latest.Version.String())
+			cmd.Printf("Successfully updated to version %s\n", nvs.Version.String())
 			cmd.Println()
 			cmd.Println("Release Notes:")
 			cmd.Println()
-			cmd.Println(cli.Wrap(80, latest.ReleaseNotes))
+			cmd.Println(cli.Wrap(80, nvs.ReleaseNotes))
 
 			return nil
 		},
 	}
-
+	cmd.Flags().Var(&ver, "version", "version to updated the nsc binary to")
 	return cmd
 }
 
@@ -89,8 +119,8 @@ func init() {
 	GetRootCmd().AddCommand(createUpdateCommand())
 }
 
-type UpdateCheckFn func(slug string) (*selfupdate.Release, bool, error)
-type UpdateFn func(current semver.Version, slug string) (*selfupdate.Release, error)
+type UpdateCheckFn func(slug string, wantVer string) (*selfupdate.Release, bool, error)
+type UpdateFn func(want *selfupdate.Release, cmdPath string) error
 
 var updateCheckFn UpdateCheckFn
 var updateFn UpdateFn
@@ -111,20 +141,21 @@ func (u *SelfUpdate) Run() (*semver.Version, error) {
 	if !u.shouldCheck() {
 		return nil, nil
 	}
-	version, err := u.doCheck()
+	rel, err := u.doCheck("")
 	if err != nil {
 		// stop checking for a bit
 		_ = u.updateLastChecked()
 		return nil, err
 	}
 	err = u.updateLastChecked()
-	return version, err
+	if rel == nil {
+		return nil, err
+	} else {
+		return &rel.Version, err
+	}
 }
 
 func (u *SelfUpdate) shouldCheck() bool {
-	if NscNoSelfUpdate {
-		return false
-	}
 	have := semver.MustParse(GetRootCmd().Version).String()
 	if have == "0.0.0-dev" {
 		return false
@@ -142,31 +173,48 @@ func (u *SelfUpdate) updateLastChecked() error {
 	return config.Save()
 }
 
-func (u *SelfUpdate) doCheck() (*semver.Version, error) {
+func (u *SelfUpdate) doCheck(wantVer string) (*selfupdate.Release, error) {
 	config := GetConfig()
 	have, err := semver.ParseTolerant(GetRootCmd().Version)
 	if err != nil {
 		return nil, err
 	}
+	if wantVer != "" {
+		want, err := semver.ParseTolerant(wantVer)
+		if err != nil {
+			return nil, err
+		}
+		wantVer = want.String()
+	}
 	wait := spinner.New(spinner.CharSets[14], 100*time.Millisecond)
-	wait.Prefix = "Checking for latest version "
+	if wantVer == "" {
+		wait.Prefix = "Checking for latest version "
+	} else {
+		wait.Prefix = fmt.Sprintf("Checking for version %s ", wantVer)
+	}
 	_ = wait.Color("italic")
 	wait.Start()
 	defer wait.Stop()
 
-	var latest *selfupdate.Release
+	var want *selfupdate.Release
 	var found bool
 	if updateCheckFn == nil {
 		// the library freak out if GITHUB_TOKEN is set - don't break travis :)
 		_ = os.Setenv("GITHUB_TOKEN", "")
-		latest, found, err = selfupdate.DetectLatest(config.GithubUpdates)
+		if wantVer == "" {
+			want, found, err = selfupdate.DetectLatest(config.GithubUpdates)
+		} else {
+			want, found, err = selfupdate.DetectVersion(GetConfig().GithubUpdates, wantVer)
+		}
 	} else {
-		latest, found, err = updateCheckFn(config.GithubUpdates)
+		want, found, err = updateCheckFn(config.GithubUpdates, wantVer)
 	}
 	if err != nil {
 		return nil, fmt.Errorf("error checking version: %v", err)
-	} else if found && latest.Version.GT(have) {
-		return &latest.Version, nil
+	} else if !found {
+		return nil, fmt.Errorf("version %v not found", wantVer)
+	} else if !want.Version.EQ(have) {
+		return want, nil
 	}
 	return nil, nil
 }
diff --git a/go.sum b/go.sum
index 23b2425..48c4741 100644
--- a/go.sum
+++ b/go.sum
@@ -19,7 +19,6 @@ github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=
 github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
 github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
 github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
-github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM=
 github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
 github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg=
 github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
@@ -42,7 +41,6 @@ github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNU
 github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
 github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
 github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
-github.com/kr/pty v1.1.1 h1:VkoXIwSboBpnk99O/KFauAEILuNHv5DVFKZMBN/gUgw=
 github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
 github.com/kr/pty v1.1.4 h1:5Myjjh3JY/NaAi4IsUbHADytDyl1VE1Y9PXDlL+P/VQ=
 github.com/kr/pty v1.1.4/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
@@ -60,13 +58,11 @@ github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG
 github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
 github.com/mitchellh/go-wordwrap v1.0.0 h1:6GlHJ/LTGMrIJbwgdqdl2eEH8o+Exx/0m8ir9Gns0u4=
 github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo=
-github.com/mitchellh/mapstructure v1.0.0 h1:vVpGvMXJPqSDh2VYHF7gsfQj8Ncx+Xw5Y1KHeTRY+7I=
 github.com/mitchellh/mapstructure v1.0.0/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
 github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
 github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
 github.com/nats-io/cliprompts/v2 v2.0.0-20191226174129-372d79b36768 h1:sdr8zfPeN/b4/TDoxbD9ARAtyP92zDFDvj87MeAAyjk=
 github.com/nats-io/cliprompts/v2 v2.0.0-20191226174129-372d79b36768/go.mod h1:oweZn7AeaVJYKlNHfCIhznJVsdySLSng55vfuINE/d0=
-github.com/nats-io/jwt v0.2.6 h1:eAyoYvGgGLXR2EpnsBUvi/FcFrBqN6YKFVbOoEfPN4k=
 github.com/nats-io/jwt v0.2.6/go.mod h1:mQxQ0uHQ9FhEVPIcTSKwx2lqZEpXWWcCgA7R6NrWvvY=
 github.com/nats-io/jwt v0.3.3-0.20200519195258-f2bf5ce574c7 h1:RnGotxlghqR5D2KDAu4TyuLqyjuylOsJiAFhXvMvQIc=
 github.com/nats-io/jwt v0.3.3-0.20200519195258-f2bf5ce574c7/go.mod h1:n3cvmLfBfnpV4JJRN7lRYCyZnw48ksGsbThGXEk4w9M=
@@ -74,17 +70,14 @@ github.com/nats-io/nats-server/v2 v2.0.1-0.20190625001713-2db76bde3329 h1:RjMdWl
 github.com/nats-io/nats-server/v2 v2.0.1-0.20190625001713-2db76bde3329/go.mod h1:RyVdsHHvY4B6c9pWG+uRLpZ0h0XsqiuKp2XCTurP5LI=
 github.com/nats-io/nats.go v1.8.1 h1:6lF/f1/NN6kzUDBz6pyvQDEXO39jqXcWRLu/tKjtOUQ=
 github.com/nats-io/nats.go v1.8.1/go.mod h1:BrFz9vVn0fU3AcH9Vn4Kd7W0NpJ651tD5omQ3M8LwxM=
-github.com/nats-io/nkeys v0.0.2 h1:+qM7QpgXnvDDixitZtQUBDY9w/s9mu1ghS+JIbsrx6M=
 github.com/nats-io/nkeys v0.0.2/go.mod h1:dab7URMsZm6Z/jp9Z5UGa87Uutgc2mVpXLC4B7TDb/4=
 github.com/nats-io/nkeys v0.1.4 h1:aEsHIssIk6ETN5m2/MD8Y4B2X7FfXrBAUdkyRvbVYzA=
 github.com/nats-io/nkeys v0.1.4/go.mod h1:XdZpAbhgyyODYqjTawOnIOI7VlbKSarI9Gfy1tqEu/s=
 github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw=
 github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
-github.com/onsi/ginkgo v1.6.0 h1:Ix8l273rp3QzYgXSR+c8d1fTG7UPgYkOSELPhiY/YGw=
 github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
 github.com/onsi/ginkgo v1.7.0 h1:WSHQ+IS43OoUrWtD1/bbclrwK8TTH5hzp+umCiuxHgs=
 github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
-github.com/onsi/gomega v1.4.2 h1:3mYCb7aPxS/RU7TI1y4rkEn1oKmPRjNJLNEXgw7MH2I=
 github.com/onsi/gomega v1.4.2/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
 github.com/onsi/gomega v1.4.3 h1:RE1xgDvH7imwFD45h+u2SgIfERHlS2yNG4DObb5BSKU=
 github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
@@ -98,7 +91,6 @@ github.com/russross/blackfriday v1.5.2 h1:HyvC0ARfnZBqnXwABFeSZHpKvJHJJfPz81GNue
 github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
 github.com/spf13/afero v1.1.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI=
 github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
-github.com/spf13/cast v1.2.0 h1:HHl1DSRbEQN2i8tJmtS6ViPyHx35+p51amrdsiTCrkg=
 github.com/spf13/cast v1.2.0/go.mod h1:r2rcYCSwa1IExKTDiTfzaxqT2FNHs8hODu4LnUfgKEg=
 github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8=
 github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
@@ -106,7 +98,6 @@ github.com/spf13/cobra v0.0.3 h1:ZlrZ4XsMRm04Fr5pSFxBgfND2EBVa1nLpiy1stUsX/8=
 github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
 github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk=
 github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
-github.com/spf13/pflag v1.0.2 h1:Fy0orTDgHdbnzHcsOgfCN4LtHf0ec3wwtiwJqwvf3Gc=
 github.com/spf13/pflag v1.0.2/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
 github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg=
 github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
@@ -121,31 +112,24 @@ github.com/ulikunitz/xz v0.5.5 h1:pFrO0lVpTBXLpYw+pnLj6TbvHuyjXMfjGeCwSqCVwok=
 github.com/ulikunitz/xz v0.5.5/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4ABRW8=
 github.com/xlab/tablewriter v0.0.0-20160610135559-80b567a11ad5 h1:gmD7q6cCJfBbcuobWQe/KzLsd9Cd3amS1Mq5f3uU1qo=
 github.com/xlab/tablewriter v0.0.0-20160610135559-80b567a11ad5/go.mod h1:fVwOndYN3s5IaGlMucfgxwMhqwcaJtlGejBU6zX6Yxw=
-golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9 h1:mKdxBk7AujPs8kU4m80U72y/zjbZ3UcXC7dClwKbUI0=
 golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
 golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
-golang.org/x/crypto v0.0.0-20190530122614-20be4c3c3ed5 h1:8dUaAV7K4uHsF56JQWkprecIQKdPHtR9jCHF5nB8uzc=
 golang.org/x/crypto v0.0.0-20190530122614-20be4c3c3ed5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59 h1:3zb4D3T4G8jdExgVU/95+vQXfpEPiMdCaZgmGVxjNHM=
 golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
 golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
-golang.org/x/net v0.0.0-20181108082009-03003ca0c849 h1:FSqE2GGG7wzsYUsWiQ8MZrvEd1EOyU3NCF0AW3Wtltg=
 golang.org/x/net v0.0.0-20181108082009-03003ca0c849/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
 golang.org/x/net v0.0.0-20190724013045-ca1201d0de80 h1:Ao/3l156eZf2AW5wK8a7/smtodRU+gha3+BeqJ69lRk=
 golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/oauth2 v0.0.0-20181106182150-f42d05182288 h1:JIqe8uIcRBHXDQVvZtHwp80ai3Lw3IJAeJEs55Dc1W0=
 golang.org/x/oauth2 v0.0.0-20181106182150-f42d05182288/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
 golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890 h1:uESlIz09WIHT2I+pasSXcpLYqYK8wHcdCetU3VuMBJE=
 golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
 golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20181108010431-42b317875d0f h1:Bl/8QSvNqXvPGPGXa2z5xUTmV7VDcZyvRZ+QQXkXTZQ=
 golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sys v0.0.0-20180906133057-8cf3aee42992 h1:BH3eQWeGbwRU2+wxxuuPOdFBmaiBH81O8BugSjHeTFg=
 golang.org/x/sys v0.0.0-20180906133057-8cf3aee42992/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8 h1:YoY1wS6JYVRpIfFngRf2HHo9R9dAne3xbkGOQ5rJXjU=
 golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -153,7 +137,6 @@ golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7w
 golang.org/x/sys v0.0.0-20190530182044-ad28b68e88f1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e h1:D5TXcfTk7xF7hvieo4QErS3qqCB4teTffacDWr7CI+0=
 golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
 golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
@@ -167,7 +150,6 @@ gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
 gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
 gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
 gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
-gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE=
 gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
 gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
diff --git a/main.go b/main.go
index 572f08d..1cfe849 100644
--- a/main.go
+++ b/main.go
@@ -16,24 +16,5 @@ func main() {
 		log.Fatal(err)
 	}
 	conf.SetVersion(version)
-
-	// disable update check if running a locally built version
-	if version != "0.0.0-dev" {
-		u, err := cmd.NewSelfUpdate()
-		if err != nil {
-			// not fatal
-			cmd.GetRootCmd().Printf("error checking for updates: %v\n", err)
-		}
-		if u != nil {
-			m, err := u.Run()
-			if err != nil {
-				cmd.GetRootCmd().Printf("error checking for updates: %v\n", err)
-			}
-			if m != nil {
-				cmd.GetRootCmd().Printf("new version available %s - `nsc update` to update.\n", m.String())
-			}
-		}
-	}
-
 	cmd.Execute()
 }


Test patch:
diff --git a/cmd/update_test.go b/cmd/update_test.go
index a0f88f3..bd528e7 100644
--- a/cmd/update_test.go
+++ b/cmd/update_test.go
@@ -16,6 +16,7 @@
 package cmd
 
 import (
+	"fmt"
 	"os"
 	"testing"
 	"time"
@@ -35,17 +36,16 @@ func TestUpdate_RunDoesntUpdateOrCheck(t *testing.T) {
 
 	var checkCalled bool
 	var updateCalled bool
-	updateCheckFn = func(slug string) (*selfupdate.Release, bool, error) {
+	updateCheckFn = func(slug string, wantVer string) (*selfupdate.Release, bool, error) {
+		if wantVer != "" && !semver.MustParse(wantVer).EQ(semver.MustParse("1.0.0")) {
+			return nil, false, fmt.Errorf("Expected request from 1.0.0 but got %s", wantVer)
+		}
 		checkCalled = true
-		var r selfupdate.Release
-		r.Version = semver.MustParse("1.0.0")
-		return nil, false, nil
+		return &selfupdate.Release{Version: semver.MustParse("1.0.0"), ReleaseNotes: "f33dfac3"}, true, nil
 	}
-	updateFn = func(current semver.Version, slug string) (*selfupdate.Release, error) {
+	updateFn = func(want *selfupdate.Release, cmdPath string) error {
 		updateCalled = true
-		var r selfupdate.Release
-		r.Version = semver.MustParse("1.0.0")
-		return nil, nil
+		return nil
 	}
 	defer func() {
 		require.NoError(t, os.Unsetenv(NscHomeEnv))
@@ -73,17 +73,16 @@ func TestUpdate_NeedsUpdate(t *testing.T) {
 
 	var checkCalled bool
 	var updateCalled bool
-	updateCheckFn = func(slug string) (*selfupdate.Release, bool, error) {
+	updateCheckFn = func(slug string, wantVer string) (*selfupdate.Release, bool, error) {
+		if wantVer != "" && !semver.MustParse(wantVer).EQ(semver.MustParse("1.0.0")) {
+			return nil, false, fmt.Errorf("Expected request from 1.0.0 but got %s", wantVer)
+		}
 		checkCalled = true
-		var r selfupdate.Release
-		r.Version = semver.MustParse("1.0.1")
-		return &r, true, nil
+		return &selfupdate.Release{Version: semver.MustParse("1.0.1"), ReleaseNotes: "f33dfac3"}, true, nil
 	}
-	updateFn = func(current semver.Version, slug string) (*selfupdate.Release, error) {
+	updateFn = func(want *selfupdate.Release, cmdPath string) error {
 		updateCalled = true
-		var r selfupdate.Release
-		r.Version = semver.MustParse("1.0.1")
-		return nil, nil
+		return nil
 	}
 	defer func() {
 		require.NoError(t, os.Unsetenv(NscHomeEnv))
@@ -94,7 +93,7 @@ func TestUpdate_NeedsUpdate(t *testing.T) {
 	su, err := NewSelfUpdate()
 	require.NoError(t, err)
 	require.NotNil(t, su)
-	require.True(t, su.shouldCheck())
+	require.True(t, su.shouldCheck()) // prevents updateFn from bein called
 	_, err = su.Run()
 	require.NoError(t, err)
 	require.True(t, checkCalled)
@@ -111,26 +110,16 @@ func TestUpdate_DoUpdateWithV(t *testing.T) {
 
 	var checkCalled bool
 	var updateCalled bool
-	updateCheckFn = func(slug string) (*selfupdate.Release, bool, error) {
-		checkCalled = true
-		var r selfupdate.Release
-		var err error
-		r.Version, err = semver.ParseTolerant("v1.0.1")
-		if err != nil {
-			return nil, true, err
+	updateCheckFn = func(slug string, wantVer string) (*selfupdate.Release, bool, error) {
+		if wantVer != "" && !semver.MustParse(wantVer).EQ(semver.MustParse("1.0.0")) {
+			return nil, false, fmt.Errorf("Expected request from 1.0.0 but got %s", wantVer)
 		}
-		return &r, true, nil
+		checkCalled = true
+		return &selfupdate.Release{Version: semver.MustParse("1.0.1"), ReleaseNotes: "f33dfac3"}, true, nil
 	}
-	updateFn = func(current semver.Version, slug string) (*selfupdate.Release, error) {
+	updateFn = func(want *selfupdate.Release, cmdPath string) error {
 		updateCalled = true
-		var r selfupdate.Release
-		var err error
-		r.Version, err = semver.ParseTolerant("v1.0.1")
-		if err != nil {
-			return nil, err
-		}
-		r.ReleaseNotes = "f33dfac3"
-		return &r, nil
+		return nil
 	}
 	defer func() {
 		require.NoError(t, os.Unsetenv(NscHomeEnv))
@@ -155,22 +144,16 @@ func TestUpdate_DoUpdate(t *testing.T) {
 
 	var checkCalled bool
 	var updateCalled bool
-	updateCheckFn = func(slug string) (*selfupdate.Release, bool, error) {
+	updateCheckFn = func(slug string, wantVer string) (*selfupdate.Release, bool, error) {
+		if wantVer != "" && !semver.MustParse(wantVer).EQ(semver.MustParse("1.0.0")) {
+			return nil, false, fmt.Errorf("Expected request from 1.0.0 but got %s", wantVer)
+		}
 		checkCalled = true
-		var r selfupdate.Release
-		r.Version = semver.MustParse("1.0.1")
-		return &r, true, nil
+		return &selfupdate.Release{Version: semver.MustParse("1.0.1"), ReleaseNotes: "f33dfac3"}, true, nil
 	}
-	updateFn = func(current semver.Version, slug string) (*selfupdate.Release, error) {
+	updateFn = func(want *selfupdate.Release, cmdPath string) error {
 		updateCalled = true
-		var r selfupdate.Release
-		var err error
-		r.Version, err = semver.ParseTolerant("1.0.1")
-		if err != nil {
-			return nil, err
-		}
-		r.ReleaseNotes = "f33dfac3"
-		return &r, nil
+		return nil
 	}
 	defer func() {
 		require.NoError(t, os.Unsetenv(NscHomeEnv))
@@ -185,44 +168,103 @@ func TestUpdate_DoUpdate(t *testing.T) {
 	require.Contains(t, stderr, "f33dfac3")
 }
 
-func TestUpdate_BypassedByEnv(t *testing.T) {
+func TestUpdate_VerPresent(t *testing.T) {
 	d := MakeTempDir(t)
 	require.NoError(t, os.Setenv(NscHomeEnv, d))
-	require.NoError(t, os.Setenv(NscNoSelfUpdateEnv, "true"))
-	SetEnvOptions()
-
 	conf := GetConfig()
 	conf.GithubUpdates = "foo/bar"
-	conf.SetVersion("1.0.0")
+	conf.SetVersion("v1.0.0")
 	conf.LastUpdate = time.Date(2018, 1, 1, 0, 0, 0, 0, time.UTC).Unix()
 
 	var checkCalled bool
 	var updateCalled bool
-	updateCheckFn = func(slug string) (*selfupdate.Release, bool, error) {
+	updateCheckFn = func(slug string, wantVer string) (*selfupdate.Release, bool, error) {
+		if !semver.MustParse(wantVer).EQ(semver.MustParse("2.0.0")) {
+			return nil, false, fmt.Errorf("Expected request from 2.0.0 but got %s", wantVer)
+		}
 		checkCalled = true
-		var r selfupdate.Release
-		r.Version = semver.MustParse("1.0.1")
-		return &r, true, nil
+		return &selfupdate.Release{Version: semver.MustParse("2.0.0"), ReleaseNotes: "f33dfac3"}, true, nil
 	}
-	updateFn = func(current semver.Version, slug string) (*selfupdate.Release, error) {
+	updateFn = func(want *selfupdate.Release, cmdPath string) error {
 		updateCalled = true
-		var r selfupdate.Release
-		r.Version = semver.MustParse("1.0.1")
-		return nil, nil
+		return nil
 	}
 	defer func() {
 		require.NoError(t, os.Unsetenv(NscHomeEnv))
-		require.NoError(t, os.Unsetenv(NscNoSelfUpdateEnv))
 		updateCheckFn = nil
 		updateFn = nil
 	}()
 
-	su, err := NewSelfUpdate()
+	_, stderr, err := ExecuteCmd(createUpdateCommand(), "--version", "2.0.0")
 	require.NoError(t, err)
-	require.NotNil(t, su)
-	require.False(t, su.shouldCheck())
-	_, err = su.Run()
+	require.True(t, checkCalled)
+	require.True(t, updateCalled)
+	require.Contains(t, stderr, "f33dfac3")
+}
+
+func TestUpdate_VerSame(t *testing.T) {
+	d := MakeTempDir(t)
+	require.NoError(t, os.Setenv(NscHomeEnv, d))
+	conf := GetConfig()
+	conf.GithubUpdates = "foo/bar"
+	conf.SetVersion("v1.0.0")
+	conf.LastUpdate = time.Date(2018, 1, 1, 0, 0, 0, 0, time.UTC).Unix()
+
+	var checkCalled bool
+	var updateCalled bool
+	updateCheckFn = func(slug string, wantVer string) (*selfupdate.Release, bool, error) {
+		if !semver.MustParse(wantVer).EQ(semver.MustParse("1.0.0")) {
+			return nil, false, fmt.Errorf("Expected request from 2.0.0 but got %s", wantVer)
+		}
+		checkCalled = true
+		return &selfupdate.Release{Version: semver.MustParse("1.0.0"), ReleaseNotes: "f33dfac3"}, true, nil
+	}
+	updateFn = func(want *selfupdate.Release, cmdPath string) error {
+		updateCalled = true
+		return nil
+	}
+	defer func() {
+		require.NoError(t, os.Unsetenv(NscHomeEnv))
+		updateCheckFn = nil
+		updateFn = nil
+	}()
+
+	_, _, err := ExecuteCmd(createUpdateCommand(), "--version", "1.0.0")
 	require.NoError(t, err)
-	require.False(t, checkCalled)
+	require.True(t, checkCalled)
+	require.False(t, updateCalled)
+}
+
+func TestUpdate_VerNotFound(t *testing.T) {
+	d := MakeTempDir(t)
+	require.NoError(t, os.Setenv(NscHomeEnv, d))
+	conf := GetConfig()
+	conf.GithubUpdates = "foo/bar"
+	conf.SetVersion("v1.0.0")
+	conf.LastUpdate = time.Date(2018, 1, 1, 0, 0, 0, 0, time.UTC).Unix()
+
+	var checkCalled bool
+	var updateCalled bool
+	updateCheckFn = func(slug string, wantVer string) (*selfupdate.Release, bool, error) {
+		if !semver.MustParse(wantVer).EQ(semver.MustParse("2.0.0")) {
+			return nil, false, fmt.Errorf("Expected request from 2.0.0 but got %s", wantVer)
+		}
+		checkCalled = true
+		return nil, false, nil
+	}
+	updateFn = func(want *selfupdate.Release, cmdPath string) error {
+		updateCalled = true
+		return nil
+	}
+	defer func() {
+		require.NoError(t, os.Unsetenv(NscHomeEnv))
+		updateCheckFn = nil
+		updateFn = nil
+	}()
+
+	_, stderr, err := ExecuteCmd(createUpdateCommand(), "--version", "2.0.0")
+	require.Error(t, err)
+	require.True(t, checkCalled)
 	require.False(t, updateCalled)
+	require.Contains(t, stderr, "version 2.0.0 not found")
 }