diff --git a/cmd/tailscale/cli/status.go b/cmd/tailscale/cli/status.go index cb8755bc2..d70463b8f 100644 --- a/cmd/tailscale/cli/status.go +++ b/cmd/tailscale/cli/status.go @@ -236,6 +236,9 @@ func runStatus(ctx context.Context, args []string) error { printHealth() } printFunnelStatus(ctx) + if cv := st.ClientVersion; cv != nil && !cv.RunningLatest && cv.LatestVersion != "" { + printf("# New Tailscale version is available: %q, run `tailscale update` to update.\n", cv.LatestVersion) + } return nil } diff --git a/ipn/ipnlocal/local.go b/ipn/ipnlocal/local.go index 13f88db04..6e3bfe032 100644 --- a/ipn/ipnlocal/local.go +++ b/ipn/ipnlocal/local.go @@ -268,6 +268,9 @@ type LocalBackend struct { // at the moment that tkaSyncLock is taken). tkaSyncLock sync.Mutex clock tstime.Clock + + // Last ClientVersion received in MapResponse, guarded by mu. + lastClientVersion *tailcfg.ClientVersion } type updateStatus struct { @@ -671,6 +674,9 @@ func (b *LocalBackend) updateStatus(sb *ipnstate.StatusBuilder, extraLocked func s.TUN = !b.sys.IsNetstack() s.BackendState = b.state.String() s.AuthURL = b.authURLSticky + if prefs := b.pm.CurrentPrefs(); prefs.Valid() && prefs.AutoUpdate().Check { + s.ClientVersion = b.lastClientVersion + } if err := health.OverallError(); err != nil { switch e := err.(type) { case multierr.Error: @@ -2181,6 +2187,9 @@ func (b *LocalBackend) tellClientToBrowseToURL(url string) { // onClientVersion is called on MapResponse updates when a MapResponse contains // a non-nil ClientVersion message. func (b *LocalBackend) onClientVersion(v *tailcfg.ClientVersion) { + b.mu.Lock() + b.lastClientVersion = v + b.mu.Unlock() switch runtime.GOOS { case "darwin", "ios": // These auto-update well enough, and we haven't converted the diff --git a/ipn/ipnstate/ipnstate.go b/ipn/ipnstate/ipnstate.go index 7e3a10b4a..299ddbde2 100644 --- a/ipn/ipnstate/ipnstate.go +++ b/ipn/ipnstate/ipnstate.go @@ -71,6 +71,8 @@ type Status struct { Peer map[key.NodePublic]*PeerStatus User map[tailcfg.UserID]tailcfg.UserProfile + + ClientVersion *tailcfg.ClientVersion } // TKAKey describes a key trusted by network lock. diff --git a/tailcfg/tailcfg.go b/tailcfg/tailcfg.go index 501a6bf62..0c84b8b15 100644 --- a/tailcfg/tailcfg.go +++ b/tailcfg/tailcfg.go @@ -110,7 +110,8 @@ type CapabilityVersion int // - 70: 2023-08-16: removed most Debug fields; added NodeAttrDisable*, NodeAttrDebug* instead // - 71: 2023-08-17: added NodeAttrOneCGNATEnable, NodeAttrOneCGNATDisable // - 72: 2023-08-23: TS-2023-006 UPnP issue fixed; UPnP can now be used again -const CurrentCapabilityVersion CapabilityVersion = 72 +// - 73: 2023-09-01: Non-Windows clients expect to receive ClientVersion +const CurrentCapabilityVersion CapabilityVersion = 73 type StableID string