diff --git a/pkg/argocd/git.go b/pkg/argocd/git.go index 8ec22744..7710b98c 100644 --- a/pkg/argocd/git.go +++ b/pkg/argocd/git.go @@ -4,6 +4,7 @@ import ( "bytes" "crypto/sha256" "encoding/hex" + "errors" "fmt" "os" "path" @@ -12,6 +13,7 @@ import ( "sigs.k8s.io/kustomize/api/konfig" "sigs.k8s.io/kustomize/api/types" + "sigs.k8s.io/kustomize/kyaml/kio" "sigs.k8s.io/kustomize/kyaml/order" kyaml "sigs.k8s.io/kustomize/kyaml/yaml" @@ -345,43 +347,83 @@ func writeKustomization(app *v1alpha1.Application, wbc *WriteBackConfig, gitC gi } // updateKustomizeFile reads the kustomization file at path, applies the filter to it, and writes the result back -// to the file. This is the same behavior as kyaml.UpdateFile, but it preserves the original order -// of YAML fields to minimize git diffs. +// to the file. This is the same behavior as kyaml.UpdateFile, but it preserves the original order of YAML fields +// and indentation of YAML sequences to minimize git diffs. func updateKustomizeFile(filter kyaml.Filter, path string) (error, bool) { - // Read the yaml - y, err := kyaml.ReadFile(path) + // Open the input file for read + yRaw, err := os.ReadFile(path) if err != nil { return err, false } - originalData, err := y.String() + // Read the yaml document from bytes + originalYSlice, err := kio.FromBytes(yRaw) if err != nil { return err, false } + // Check that we are dealing with a single document + if len(originalYSlice) != 1 { + return errors.New("target parameter file should contain a single YAML document"), false + } + originalY := originalYSlice[0] + + // Get the (parsed) original document + originalData, err := originalY.String() + if err != nil { + return err, false + } + + // Create a reader, preserving indentation of sequences + var out bytes.Buffer + rw := &kio.ByteReadWriter{ + Reader: bytes.NewBuffer(yRaw), + Writer: &out, + PreserveSeqIndent: true, + } + + // Read from input buffer + newYSlice, err := rw.Read() + if err != nil { + return err, false + } + // We can safely assume we have a single document from the previous check + newY := newYSlice[0] + // Update the yaml - yCpy := y.Copy() - if err := yCpy.PipeE(filter); err != nil { + if err := newY.PipeE(filter); err != nil { return err, false } // Preserve the original order of fields - if err := order.SyncOrder(y, yCpy); err != nil { + if err := order.SyncOrder(originalY, newY); err != nil { return err, false } - override, err := yCpy.String() + // Write the yaml document to the output buffer + if err = rw.Write([]*kyaml.RNode{newY}); err != nil { + return err, false + } + + // newY contains metadata used by kio to preserve sequence indentation, + // hence we need to parse the output buffer instead + newParsedY, err := kyaml.Parse(out.String()) + if err != nil { + return err, false + } + newData, err := newParsedY.String() if err != nil { return err, false } - if originalData == override { + // Compare the updated document with the original document + if originalData == newData { log.Debugf("target parameter file and marshaled data are the same, skipping commit.") return nil, true } - // Write the yaml - if err := os.WriteFile(path, []byte(override), 0600); err != nil { + // Write to file the changes + if err := os.WriteFile(path, out.Bytes(), 0600); err != nil { return err, false } diff --git a/pkg/argocd/git_test.go b/pkg/argocd/git_test.go index cfd79148..7db7aad5 100644 --- a/pkg/argocd/git_test.go +++ b/pkg/argocd/git_test.go @@ -267,6 +267,18 @@ func Test_updateKustomizeFile(t *testing.T) { wantContent: `images: - name: foo digest: sha23456 +`, + filter: filter, + }, + { + name: "indented", + content: `images: + - name: foo + digest: sha12345 +`, + wantContent: `images: + - name: foo + digest: sha23456 `, filter: filter, },