diff --git a/pkg/config/profile.go b/pkg/config/profile.go index cb9dd2b0..61156cd5 100644 --- a/pkg/config/profile.go +++ b/pkg/config/profile.go @@ -60,7 +60,7 @@ var KeyRing keyring.Keyring // CreateProfile creates a profile when logging in func (p *Profile) CreateProfile() error { - // Remove existing profile first + // Remove all keys under existing profile first v := p.deleteProfile(viper.GetViper()) writeErr := p.writeProfile(v) @@ -72,7 +72,16 @@ func (p *Profile) CreateProfile() error { } func (p *Profile) deleteProfile(v *viper.Viper) *viper.Viper { - return p.safeRemove(v, p.ProfileName) + for _, key := range v.AllKeys() { + if strings.HasPrefix(key, p.ProfileName+".") { + newViper, err := removeKey(v, key) + if err == nil { + // failure to remove a key should not break the login flow + v = newViper + } + } + } + return v } // GetColor gets the color setting for the user based on the flag or the @@ -342,9 +351,6 @@ func (p *Profile) writeProfile(runtimeViper *viper.Viper) error { runtimeViper = p.safeRemove(runtimeViper, "publishable_key") } - // Remove experimental fields during login - runtimeViper = p.removeExperimentalFields(runtimeViper) - runtimeViper.SetConfigFile(profilesFile) // Ensure we preserve the config file type @@ -510,11 +516,6 @@ func (p *Profile) GetExperimentalFields() ExperimentalFields { } } -func (p *Profile) removeExperimentalFields(v *viper.Viper) *viper.Viper { - v = p.safeRemove(v, experimentalPrefix) - return v -} - // SessionCredentials are the credentials needed for this session type SessionCredentials struct { UAT string `json:"uat"` diff --git a/pkg/config/profile_test.go b/pkg/config/profile_test.go index 42e0ae1d..da890f9b 100644 --- a/pkg/config/profile_test.go +++ b/pkg/config/profile_test.go @@ -143,6 +143,65 @@ func TestExperimentalFields(t *testing.T) { cleanUp(c.ProfilesFile) } +func TestOldProfileDeleted(t *testing.T) { + profilesFile := filepath.Join(os.TempDir(), "stripe", "config.toml") + p := Profile{ + ProfileName: "test", + DeviceName: "device-before-test", + TestModeAPIKey: "sk_test_123", + DisplayName: "display-name-before-test", + } + c := &Config{ + Color: "auto", + LogLevel: "info", + Profile: p, + ProfilesFile: profilesFile, + } + c.InitConfig() + + p.WriteConfigField("experimental.stripe_headers", "test-headers") + + v := viper.New() + + v.SetConfigFile(profilesFile) + err := p.writeProfile(v) + require.NoError(t, err) + + untouchedProfile := Profile{ + ProfileName: "foo", + DeviceName: "foo-device-name", + TestModeAPIKey: "foo_test_123", + } + err = untouchedProfile.writeProfile(v) + require.NoError(t, err) + + p = Profile{ + ProfileName: "test", + DeviceName: "device-after-test", + TestModeAPIKey: "sk_test_456", + DisplayName: "", + } + + v = p.deleteProfile(v) + err = p.writeProfile(v) + require.NoError(t, err) + + require.FileExists(t, c.ProfilesFile) + + // Overwrites keys + require.Equal(t, "device-after-test", v.GetString(p.GetConfigField(DeviceNameName))) + require.Equal(t, "sk_test_456", v.GetString(p.GetConfigField(TestModeAPIKeyName))) + require.Equal(t, "", v.GetString(p.GetConfigField(DisplayNameName))) + // Deletes nested keys + require.False(t, v.IsSet(v.GetString(p.GetConfigField("experimental.stripe_headers")))) + require.False(t, v.IsSet(v.GetString(p.GetConfigField("experimental")))) + // Leaves the other profile untouched + require.Equal(t, "foo-device-name", v.GetString(untouchedProfile.GetConfigField(DeviceNameName))) + require.Equal(t, "foo_test_123", v.GetString(untouchedProfile.GetConfigField(TestModeAPIKeyName))) + + cleanUp(c.ProfilesFile) +} + func helperLoadBytes(t *testing.T, name string) []byte { bytes, err := os.ReadFile(name) if err != nil {