From 7df21697ef14b74ae06bc0502a6e18ed8cc22620 Mon Sep 17 00:00:00 2001 From: Gabriel Martinho Date: Sat, 18 Jul 2026 12:11:08 -0300 Subject: [PATCH] feat(cli): allow suppressing update notifier via SUPABASE_NO_UPDATE_NOTIFIER The CLI checks GitHub for a newer release after every command and prints an upgrade suggestion to stderr. Users who pin the CLI version through a package manager such as Nix have no way to opt out of this check. Add a SUPABASE_NO_UPDATE_NOTIFIER environment variable that, when set to a truthy value, skips both the release check and the suggestion. The variable is read directly from the environment rather than through viper because the upgrade check also runs for --help and --version, which bypass the cobra initializer that configures viper's automatic env binding. Closes #5853 --- apps/cli-go/cmd/root.go | 22 ++++++++++++++++------ apps/cli-go/cmd/root_test.go | 19 +++++++++++++++++++ 2 files changed, 35 insertions(+), 6 deletions(-) diff --git a/apps/cli-go/cmd/root.go b/apps/cli-go/cmd/root.go index f83eb6d11b..227de8656a 100644 --- a/apps/cli-go/cmd/root.go +++ b/apps/cli-go/cmd/root.go @@ -8,6 +8,7 @@ import ( "net/url" "os" "os/signal" + "strconv" "strings" "sync" "time" @@ -188,15 +189,17 @@ func Execute() { if executedCmd != nil { ctx = executedCmd.Context() } - version, err := checkUpgrade(ctx, afero.NewOsFs()) - if err != nil { - fmt.Fprintln(utils.GetDebugLogger(), err) - } if hint := utils.SuggestClaudePlugin(); hint != "" { fmt.Fprintln(os.Stderr, hint) } - if semver.Compare(version, "v"+utils.Version) > 0 { - fmt.Fprintln(os.Stderr, suggestUpgrade(version)) + if updateNotifierEnabled() { + version, err := checkUpgrade(ctx, afero.NewOsFs()) + if err != nil { + fmt.Fprintln(utils.GetDebugLogger(), err) + } + if semver.Compare(version, "v"+utils.Version) > 0 { + fmt.Fprintln(os.Stderr, suggestUpgrade(version)) + } } if len(utils.CmdSuggestion) > 0 { fmt.Fprintln(os.Stderr, utils.CmdSuggestion) @@ -239,6 +242,13 @@ func exitCode(err error) int { return 0 } +func updateNotifierEnabled() bool { + // Read the env directly instead of viper: the upgrade check also runs for + // --help and --version, which skip the cobra initializer that binds env vars. + disabled, _ := strconv.ParseBool(os.Getenv("SUPABASE_NO_UPDATE_NOTIFIER")) + return !disabled +} + func checkUpgrade(ctx context.Context, fsys afero.Fs) (string, error) { if shouldFetchRelease(fsys) { version, err := utils.GetLatestRelease(ctx) diff --git a/apps/cli-go/cmd/root_test.go b/apps/cli-go/cmd/root_test.go index fa7c6f39d7..ee52d8fc54 100644 --- a/apps/cli-go/cmd/root_test.go +++ b/apps/cli-go/cmd/root_test.go @@ -61,6 +61,25 @@ func TestCommandAnalyticsContext(t *testing.T) { assert.NotEmpty(t, ctx.RunID) } +func TestUpdateNotifierEnabled(t *testing.T) { + for value, wantEnabled := range map[string]bool{ + "": true, + "0": true, + "false": true, + "1": false, + "true": false, + "t": false, + "TRUE": false, + "yes": true, + } { + t.Run("value "+value, func(t *testing.T) { + t.Setenv("SUPABASE_NO_UPDATE_NOTIFIER", value) + + assert.Equal(t, wantEnabled, updateNotifierEnabled()) + }) + } +} + func TestCommandName(t *testing.T) { root := &cobra.Command{Use: "supabase"} parent := &cobra.Command{Use: "db"}