Skip to content

Commit b43859d

Browse files
committed
fix: preserve indent if possible
It's possible to preserve the indent in the original yaml when we Unmarshal to a yaml.Node, and the structure of the original yaml allows us to guess the indent. The two easiest cases are when we have a nested block-style mapping, or a sequence inside a mapping and the sequence is indented. For those, the node tells us the parsed column (starting at 1) and we use that indent. However: if we parse to some other data structure (as we do with Kustomize) or the original yaml has some structure where the indentation is unclear, eg: root: - unguessable indent - deeper: guessable: but requires deeper traversal ... then we just guess '2'. This should generate _fewer_ useless whitespace changes, but cannot prevent them all. Signed-off-by: Brian Ewins <bewins@nerdwallet.com>
1 parent b49a6c3 commit b43859d

File tree

2 files changed

+89
-1
lines changed

2 files changed

+89
-1
lines changed

pkg/argocd/update.go

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -423,6 +423,7 @@ func marshalWithIndent(in interface{}, indent int) (out []byte, err error) {
423423
var b bytes.Buffer
424424
encoder := yaml.NewEncoder(&b)
425425
defer encoder.Close()
426+
// note: yaml.v3 will only respect indents from 1 to 9 inclusive.
426427
encoder.SetIndent(indent)
427428
if err = encoder.Encode(in); err != nil {
428429
return nil, err
@@ -433,6 +434,30 @@ func marshalWithIndent(in interface{}, indent int) (out []byte, err error) {
433434
return b.Bytes(), nil
434435
}
435436

437+
func guessIndent(root *yaml.Node) int {
438+
node := root
439+
if root.Kind == yaml.DocumentNode {
440+
if len(node.Content) == 0 {
441+
return 2
442+
}
443+
node = root.Content[0]
444+
}
445+
// anything other than a map at the root makes guessing difficult
446+
if node.Kind != yaml.MappingNode || len(node.Content) == 0 {
447+
return 2
448+
}
449+
// first level map entries that are themselves mappings or sequences,
450+
// in block style, and are indented, allow guessing the preferred indent.
451+
for i, child := range node.Content {
452+
if i%2 == 1 && child.Column > 1 && child.Column < 10 && child.Style != yaml.FlowStyle {
453+
if child.Kind == yaml.MappingNode || child.Kind == yaml.SequenceNode {
454+
return child.Column - 1
455+
}
456+
}
457+
}
458+
return 2
459+
}
460+
436461
// marshalParamsOverride marshals the parameter overrides of a given application
437462
// into YAML bytes
438463
func marshalParamsOverride(app *v1alpha1.Application, originalData []byte) ([]byte, error) {
@@ -479,6 +504,7 @@ func marshalParamsOverride(app *v1alpha1.Application, originalData []byte) ([]by
479504
if err != nil {
480505
return nil, err
481506
}
507+
indent := guessIndent(&helmNewValues)
482508

483509
for _, c := range images {
484510
if c.ImageAlias == "" {
@@ -520,7 +546,7 @@ func marshalParamsOverride(app *v1alpha1.Application, originalData []byte) ([]by
520546
}
521547
}
522548

523-
override, err = marshalWithIndent(&helmNewValues, 2)
549+
override, err = marshalWithIndent(&helmNewValues, indent)
524550
} else {
525551
var params helmOverride
526552
newParams := helmOverride{

pkg/argocd/update_test.go

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2439,6 +2439,68 @@ image:
24392439
assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(string(output)))
24402440
})
24412441

2442+
t.Run("Indentation is guessed from nested mappings", func(t *testing.T) {
2443+
expected := `
2444+
unguessable:
2445+
- unguessable
2446+
image:
2447+
attributes:
2448+
tag: v2.0.0
2449+
`
2450+
2451+
inputData := []byte(`
2452+
unguessable:
2453+
- unguessable
2454+
image:
2455+
attributes:
2456+
tag: v1.0.0
2457+
`)
2458+
input := yaml.Node{}
2459+
err := yaml.Unmarshal(inputData, &input)
2460+
require.NoError(t, err)
2461+
indent := guessIndent(&input)
2462+
2463+
key := "image.attributes.tag"
2464+
value := "v2.0.0"
2465+
2466+
err = setHelmValue(&input, key, value)
2467+
require.NoError(t, err)
2468+
2469+
output, err := marshalWithIndent(&input, indent)
2470+
require.NoError(t, err)
2471+
assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(string(output)))
2472+
})
2473+
2474+
t.Run("Indentation is guessed from indented lists", func(t *testing.T) {
2475+
expected := `
2476+
unguessable: [unguessable]
2477+
guessable:
2478+
- guessable
2479+
image:
2480+
attributes:
2481+
tag: v2.0.0
2482+
`
2483+
2484+
inputData := []byte(`
2485+
unguessable: [unguessable]
2486+
guessable:
2487+
- guessable
2488+
`)
2489+
input := yaml.Node{}
2490+
err := yaml.Unmarshal(inputData, &input)
2491+
require.NoError(t, err)
2492+
indent := guessIndent(&input)
2493+
2494+
key := "image.attributes.tag"
2495+
value := "v2.0.0"
2496+
2497+
err = setHelmValue(&input, key, value)
2498+
require.NoError(t, err)
2499+
2500+
output, err := marshalWithIndent(&input, indent)
2501+
require.NoError(t, err)
2502+
assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(string(output)))
2503+
})
24422504
}
24432505

24442506
func Test_GetWriteBackConfig(t *testing.T) {

0 commit comments

Comments
 (0)