fix(cmdutils): Avoid race on global viper state in GroupOverride
Description
GroupOverride in internal/cmdutils/flag.go called viper.SetEnvPrefix and viper.BindEnv on every invocation. These functions write to a single global *Viper object shared across the entire process. Go maps are not goroutine-safe, so when parallel tests called GroupOverride concurrently, they raced on v.env , the internal map viper uses to store env bindings.
The fix introduces a package-level private *viper.Viper instance dedicated solely to resolving GITLAB_GROUP. It is initialised once at package load time using MustBindEnv for explicit error signalling. Because no other goroutine holds a reference to it, concurrent calls to GroupOverride are race-safe.
Updates
Following an architectural suggestion, the implementation was updated to use a private *viper.Viper instance instead of the global one.
The original approach of moving SetEnvPrefix and BindEnv into an init function fixed the race but introduced a subtle regression: any call to viper.Reset() would wipe the global instance, causing GroupOverride to silently return an empty string for GITLAB_GROUP. The original code was resilient to this because it re-applied the setup on every call.
The new approach uses a dedicated package-level *viper.Viper instance that is unreachable by viper.Reset() or any other global viper call, giving both race safety and resilience to global state resets.
Related Issues
Resolves #8258 (closed)
How has this been tested?
The race was reproduced locally using the -race flag and -count=5 to increase the chance of goroutine overlap:
go test -race -count=5 ./internal/commands/token/rotate/...Before the fix this reliably produced:
WARNING: DATA RACE
Write at ... github.com/spf13/viper.(*Viper).BindEnv()
gitlab.com/gitlab-org/cli/internal/cmdutils.GroupOverride()
internal/cmdutils/flag.go:11
Previous write at ... github.com/spf13/viper.(*Viper).BindEnv()
gitlab.com/gitlab-org/cli/internal/cmdutils.GroupOverride()
internal/cmdutils/flag.go:11
FAIL gitlab.com/gitlab-org/cli/internal/commands/token/rotateAfter the fix all runs pass cleanly:
go test -race -count=5 ./internal/commands/token/rotate/... ./internal/cmdutils/...
ok gitlab.com/gitlab-org/cli/internal/commands/token/rotate 1.260s
ok gitlab.com/gitlab-org/cli/internal/cmdutils 13.697s