diff --git a/clientupdate/clientupdate.go b/clientupdate/clientupdate.go index a12b4e37f..3dc01477e 100644 --- a/clientupdate/clientupdate.go +++ b/clientupdate/clientupdate.go @@ -202,7 +202,9 @@ func (up *Updater) getUpdateFunction() (fn updateFunction, canAutoUpdate bool) { // support auto-updates. return up.updateMacAppStore, false case version.IsMacSysExt(): - return up.updateMacSys, true + // Macsys update func kicks off Sparkle. Auto-updates are done by + // Sparkle. + return up.updateMacSys, false default: return nil, false } diff --git a/cmd/tailscale/cli/set.go b/cmd/tailscale/cli/set.go index a10fd771f..943abb1de 100644 --- a/cmd/tailscale/cli/set.go +++ b/cmd/tailscale/cli/set.go @@ -9,6 +9,7 @@ "flag" "fmt" "net/netip" + "os/exec" "github.com/peterbourgon/ff/v3/ffcli" "tailscale.com/clientupdate" @@ -17,6 +18,7 @@ "tailscale.com/net/tsaddr" "tailscale.com/safesocket" "tailscale.com/types/views" + "tailscale.com/version" ) var setCmd = &ffcli.Command{ @@ -157,9 +159,22 @@ func runSet(ctx context.Context, args []string) (retErr error) { } } if maskedPrefs.AutoUpdateSet { - _, err := clientupdate.NewUpdater(clientupdate.Arguments{ForAutoUpdate: true}) - if errors.Is(err, errors.ErrUnsupported) { - return errors.New("automatic updates are not supported on this platform") + // On macsys, tailscaled will set the Sparkle auto-update setting. It + // does not use clientupdate. + if version.IsMacSysExt() { + apply := "0" + if maskedPrefs.AutoUpdate.Apply { + apply = "1" + } + out, err := exec.Command("defaults", "write", "io.tailscale.ipn.macsys", "SUAutomaticallyUpdate", apply).CombinedOutput() + if err != nil { + return fmt.Errorf("failed to enable automatic updates: %v, %q", err, out) + } + } else { + _, err := clientupdate.NewUpdater(clientupdate.Arguments{ForAutoUpdate: true}) + if errors.Is(err, errors.ErrUnsupported) { + return errors.New("automatic updates are not supported on this platform") + } } } checkPrefs := curPrefs.Clone() diff --git a/version/prop.go b/version/prop.go index 138734c30..f47b6ebe2 100644 --- a/version/prop.go +++ b/version/prop.go @@ -59,6 +59,9 @@ func IsMacSysExt() bool { return false } return isMacSysExt.Get(func() bool { + if strings.Contains(os.Getenv("HOME"), "/Containers/io.tailscale.ipn.macsys/") { + return true + } exe, err := os.Executable() if err != nil { return false @@ -76,6 +79,12 @@ func IsMacAppStore() bool { return false } return isMacAppStore.Get(func() bool { + // Both macsys and app store versions can run CLI executable with + // suffix /Contents/MacOS/Tailscale. Check $HOME to filter out running + // as macsys. + if strings.Contains(os.Getenv("HOME"), "/Containers/io.tailscale.ipn.macsys/") { + return false + } exe, err := os.Executable() if err != nil { return false