@@ -13,7 +13,7 @@ import (
1313 "github.com/pivotal-cf/kiln/pkg/proofing"
1414
1515 yamlConverter "github.com/ghodss/yaml"
16- "gopkg.in/yaml.v2 "
16+ "gopkg.in/yaml.v3 "
1717)
1818
1919const (
@@ -274,8 +274,8 @@ func (i Interpolator) interpolateValueIntoYAML(input InterpolateInput, name stri
274274 return "" , err // should never happen
275275 }
276276
277- if stringScalar , ok := val .(string ); ok {
278- return stringScalar , nil
277+ if stringVal , ok := val .(string ); ok {
278+ return i . yamlMarshalString ( stringVal )
279279 }
280280
281281 interpolatedYAML , err := i .interpolate (input , name , initialYAML )
@@ -291,6 +291,39 @@ func (i Interpolator) interpolateValueIntoYAML(input InterpolateInput, name stri
291291 return string (inlinedYAML ), nil
292292}
293293
294+ // yamlMarshalString escapes a string so it can be embedded into a YAML document literally
295+ //
296+ // For example:
297+ // - "hello" → "hello"
298+ // - "true" → "true"
299+ // - "[one, 2]" → "\"[one, 2]\""
300+ func (i Interpolator ) yamlMarshalString (val string ) (string , error ) {
301+ var node yaml.Node
302+ err := yaml .Unmarshal ([]byte (val ), & node )
303+ if err != nil {
304+ return "" , err
305+ }
306+
307+ // when it's a document node jump to the content node
308+ contentNode := & node
309+ if len (node .Content ) > 0 {
310+ contentNode = node .Content [0 ]
311+ }
312+
313+ // when the node is a scalar, e.g. string, int, double, etc., no escaping is needed
314+ if contentNode .Kind == yaml .ScalarNode {
315+ return val , nil
316+ }
317+
318+ // escape objects and arrays
319+ var escaped []byte
320+ escaped , err = yaml .Marshal (val )
321+ if err != nil {
322+ return "" , err
323+ }
324+ return string (escaped ), nil
325+ }
326+
294327// Workaround to avoid YAML indentation being incorrect when value is interpolated into the metadata
295328func (i Interpolator ) yamlMarshalOneLine (yamlContents []byte ) ([]byte , error ) {
296329 return yamlConverter .YAMLToJSON (yamlContents )
0 commit comments