From 9a0ab2fe281cbd7019e0907457dcc13f8161be54 Mon Sep 17 00:00:00 2001 From: stantheman0128 Date: Fri, 10 Apr 2026 23:34:59 +0800 Subject: [PATCH] fix(variable): use StringArrayVar instead of StringToStringVar for --key flag StringToStringVar uses an internal CSV parser that cannot handle values containing ${}, commas, or other special characters. This causes values like ${POSTGRESQL.POSTGRES_CONNECTION_STRING} to be truncated or emptied. Switch to StringArrayVar and manually split each entry on the first "=" only, preserving arbitrary characters in values. Fixes #201 Co-Authored-By: Claude Sonnet 4.6 --- internal/cmd/variable/create/create.go | 30 ++++++++++++++++++++++++- internal/cmd/variable/update/update.go | 31 +++++++++++++++++++++++++- 2 files changed, 59 insertions(+), 2 deletions(-) diff --git a/internal/cmd/variable/create/create.go b/internal/cmd/variable/create/create.go index b62fe4a..449418e 100644 --- a/internal/cmd/variable/create/create.go +++ b/internal/cmd/variable/create/create.go @@ -16,6 +16,7 @@ type Options struct { id string name string environmentID string + rawKeys []string keys map[string]string skipConfirm bool inputDone bool @@ -36,12 +37,16 @@ func NewCmdCreateVariable(f *cmdutil.Factory) *cobra.Command { util.AddServiceParam(cmd, &opts.id, &opts.name) util.AddEnvOfServiceParam(cmd, &opts.environmentID) cmd.Flags().BoolVarP(&opts.skipConfirm, "yes", "y", false, "Skip confirmation") - cmd.Flags().StringToStringVarP(&opts.keys, "key", "k", nil, "Key value pair of the variable") + cmd.Flags().StringArrayVarP(&opts.rawKeys, "key", "k", nil, "Key value pair of the variable (KEY=VALUE)") return cmd } func runCreateVariable(f *cmdutil.Factory, opts *Options) error { + if err := parseRawKeys(opts); err != nil { + return err + } + if f.Interactive { return runCreateVariableInteractive(f, opts) } else { @@ -49,6 +54,29 @@ func runCreateVariable(f *cmdutil.Factory, opts *Options) error { } } +// parseRawKeys converts the raw "KEY=VALUE" strings from --key flags +// into the keys map. It splits on the first "=" only, so values +// containing "=", "${}", commas, and other special characters are preserved. +func parseRawKeys(opts *Options) error { + if len(opts.rawKeys) == 0 { + return nil + } + + if opts.keys == nil { + opts.keys = make(map[string]string, len(opts.rawKeys)) + } + + for _, kv := range opts.rawKeys { + parts := strings.SplitN(kv, "=", 2) + if len(parts) != 2 || parts[0] == "" { + return fmt.Errorf("invalid key-value pair %q: expected KEY=VALUE format", kv) + } + opts.keys[parts[0]] = parts[1] + } + + return nil +} + func runCreateVariableInteractive(f *cmdutil.Factory, opts *Options) error { zctx := f.Config.GetContext() diff --git a/internal/cmd/variable/update/update.go b/internal/cmd/variable/update/update.go index bb8ad1b..e1946f4 100644 --- a/internal/cmd/variable/update/update.go +++ b/internal/cmd/variable/update/update.go @@ -3,6 +3,7 @@ package update import ( "context" "fmt" + "strings" "github.com/briandowns/spinner" "github.com/spf13/cobra" @@ -15,6 +16,7 @@ type Options struct { id string name string environmentID string + rawKeys []string keys map[string]string skipConfirm bool inputDone bool @@ -35,12 +37,16 @@ func NewCmdUpdateVariable(f *cmdutil.Factory) *cobra.Command { util.AddServiceParam(cmd, &opts.id, &opts.name) util.AddEnvOfServiceParam(cmd, &opts.environmentID) cmd.Flags().BoolVarP(&opts.skipConfirm, "yes", "y", false, "Skip confirmation") - cmd.Flags().StringToStringVarP(&opts.keys, "key", "k", nil, "Key value pair of the variable") + cmd.Flags().StringArrayVarP(&opts.rawKeys, "key", "k", nil, "Key value pair of the variable (KEY=VALUE)") return cmd } func runUpdateVariable(f *cmdutil.Factory, opts *Options) error { + if err := parseRawKeys(opts); err != nil { + return err + } + if f.Interactive { opts.keys = make(map[string]string) return runUpdateVariableInteractive(f, opts) @@ -48,6 +54,29 @@ func runUpdateVariable(f *cmdutil.Factory, opts *Options) error { return runUpdateVariableNonInteractive(f, opts) } +// parseRawKeys converts the raw "KEY=VALUE" strings from --key flags +// into the keys map. It splits on the first "=" only, so values +// containing "=", "${}", commas, and other special characters are preserved. +func parseRawKeys(opts *Options) error { + if len(opts.rawKeys) == 0 { + return nil + } + + if opts.keys == nil { + opts.keys = make(map[string]string, len(opts.rawKeys)) + } + + for _, kv := range opts.rawKeys { + parts := strings.SplitN(kv, "=", 2) + if len(parts) != 2 || parts[0] == "" { + return fmt.Errorf("invalid key-value pair %q: expected KEY=VALUE format", kv) + } + opts.keys[parts[0]] = parts[1] + } + + return nil +} + func runUpdateVariableInteractive(f *cmdutil.Factory, opts *Options) error { zctx := f.Config.GetContext()