From f98028c925e24f19a2df94d917cbea068a1e67f0 Mon Sep 17 00:00:00 2001 From: Flint Winters <144285679+flintwinters@users.noreply.github.com> Date: Wed, 31 Dec 2025 23:21:55 -0500 Subject: [PATCH] Unwrap scalars in shell output mode. (#2548) * feat: Add UnwrapScalar to ShellVariablesPreferences - Add UnwrapScalar boolean field to ShellVariablesPreferences struct. - Initialize UnwrapScalar to false in NewDefaultShellVariablesPreferences. - This preference will control whether shell output should be quoted or raw. * feat: Propagate unwrapScalar to ShellVariablesPreferences - In configureEncoder function, set UnwrapScalar in ConfiguredShellVariablesPreferences. - This ensures the -r flag's state is passed to the shell encoder for raw output control. * feat: Implement conditional quoting in shellVariablesEncoder - Modify doEncode method to check pe.prefs.UnwrapScalar. - If UnwrapScalar is true, output raw node.Value. - Otherwise, use quoteValue for shell-safe quoting. - This enables quote-free output for Kubernetes workflows when -r is used. * test: Add tests for UnwrapScalar in shell encoder - Introduce assertEncodesToUnwrapped helper function. - Add TestShellVariablesEncoderUnwrapScalar to verify quote-free output with -r. - Add TestShellVariablesEncoderDefaultQuoting to confirm default quoting behavior without -r. - Ensure comprehensive testing of conditional quoting logic for shell output. * remove redundant test --- cmd/utils.go | 1 + pkg/yqlib/encoder_shellvariables.go | 8 +++++- pkg/yqlib/encoder_shellvariables_test.go | 33 ++++++++++++++++++++++++ pkg/yqlib/shellvariables.go | 2 ++ 4 files changed, 43 insertions(+), 1 deletion(-) diff --git a/cmd/utils.go b/cmd/utils.go index 5ba4c32a..9dfd4e4d 100644 --- a/cmd/utils.go +++ b/cmd/utils.go @@ -207,6 +207,7 @@ func configureEncoder() (yqlib.Encoder, error) { yqlib.ConfiguredKYamlPreferences.UnwrapScalar = unwrapScalar yqlib.ConfiguredPropertiesPreferences.UnwrapScalar = unwrapScalar yqlib.ConfiguredJSONPreferences.UnwrapScalar = unwrapScalar + yqlib.ConfiguredShellVariablesPreferences.UnwrapScalar = unwrapScalar yqlib.ConfiguredYamlPreferences.ColorsEnabled = colorsEnabled yqlib.ConfiguredKYamlPreferences.ColorsEnabled = colorsEnabled diff --git a/pkg/yqlib/encoder_shellvariables.go b/pkg/yqlib/encoder_shellvariables.go index 39236703..769252ba 100644 --- a/pkg/yqlib/encoder_shellvariables.go +++ b/pkg/yqlib/encoder_shellvariables.go @@ -57,7 +57,13 @@ func (pe *shellVariablesEncoder) doEncode(w *io.Writer, node *CandidateNode, pat // let's just pick a fallback key to use if we are encoding a single scalar nonemptyPath = "value" } - _, err := io.WriteString(*w, nonemptyPath+"="+quoteValue(node.Value)+"\n") + var valueString string + if pe.prefs.UnwrapScalar { + valueString = node.Value + } else { + valueString = quoteValue(node.Value) + } + _, err := io.WriteString(*w, nonemptyPath+"="+valueString+"\n") return err case SequenceNode: for index, child := range node.Content { diff --git a/pkg/yqlib/encoder_shellvariables_test.go b/pkg/yqlib/encoder_shellvariables_test.go index d41ad663..e5a91e12 100644 --- a/pkg/yqlib/encoder_shellvariables_test.go +++ b/pkg/yqlib/encoder_shellvariables_test.go @@ -135,3 +135,36 @@ func TestShellVariablesEncoderCustomSeparatorArray(t *testing.T) { func TestShellVariablesEncoderCustomSeparatorSingleChar(t *testing.T) { assertEncodesToWithSeparator(t, "a:\n b: value", "aXb=value", "X") } + +func assertEncodesToUnwrapped(t *testing.T, yaml string, shellvars string) { + var output bytes.Buffer + writer := bufio.NewWriter(&output) + + originalUnwrapScalar := ConfiguredShellVariablesPreferences.UnwrapScalar + defer func() { + ConfiguredShellVariablesPreferences.UnwrapScalar = originalUnwrapScalar + }() + + ConfiguredShellVariablesPreferences.UnwrapScalar = true + + var encoder = NewShellVariablesEncoder() + inputs, err := readDocuments(strings.NewReader(yaml), "test.yml", 0, NewYamlDecoder(ConfiguredYamlPreferences)) + if err != nil { + panic(err) + } + node := inputs.Front().Value.(*CandidateNode) + err = encoder.Encode(writer, node) + if err != nil { + panic(err) + } + writer.Flush() + + test.AssertResult(t, shellvars, strings.TrimSuffix(output.String(), "\n")) +} + +func TestShellVariablesEncoderUnwrapScalar(t *testing.T) { + assertEncodesToUnwrapped(t, "a: Lewis Carroll", "a=Lewis Carroll") + assertEncodesToUnwrapped(t, "b: 123", "b=123") + assertEncodesToUnwrapped(t, "c: true", "c=true") + assertEncodesToUnwrapped(t, "d: value with spaces", "d=value with spaces") +} diff --git a/pkg/yqlib/shellvariables.go b/pkg/yqlib/shellvariables.go index 75801cda..d111a98a 100644 --- a/pkg/yqlib/shellvariables.go +++ b/pkg/yqlib/shellvariables.go @@ -2,11 +2,13 @@ package yqlib type ShellVariablesPreferences struct { KeySeparator string + UnwrapScalar bool } func NewDefaultShellVariablesPreferences() ShellVariablesPreferences { return ShellVariablesPreferences{ KeySeparator: "_", + UnwrapScalar: false, } }