Skip to content

Commit

Permalink
Add ability to patch yaml with comments (#349)
Browse files Browse the repository at this point in the history
  • Loading branch information
Arthur-Befumo authored Aug 28, 2024
1 parent 695b562 commit e59b3e8
Show file tree
Hide file tree
Showing 4 changed files with 61 additions and 24 deletions.
30 changes: 15 additions & 15 deletions yamlpatch/container_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ func TestContainers(t *testing.T) {
Patch: func(t *testing.T, node *yaml.Node) {
c, err := newContainer(node)
require.NoError(t, err)
valNode, err := valueToYAMLNode("newvalue")
valNode, err := valueToYAMLNode("newvalue", "")
require.NoError(t, err)
err = c.Add("newkey", valNode)
require.NoError(t, err)
Expand All @@ -100,7 +100,7 @@ newkey: newvalue
Patch: func(t *testing.T, node *yaml.Node) {
c, err := newContainer(node)
require.NoError(t, err)
valNode, err := valueToYAMLNode(map[string]interface{}{"bar": "val"})
valNode, err := valueToYAMLNode(map[string]interface{}{"bar": "val"}, "")
require.NoError(t, err)
err = c.Add("foo", valNode)
require.NoError(t, err)
Expand All @@ -116,7 +116,7 @@ foo:
Patch: func(t *testing.T, node *yaml.Node) {
c, err := newContainer(node)
require.NoError(t, err)
valNode, err := valueToYAMLNode("newvalue")
valNode, err := valueToYAMLNode("newvalue", "")
require.NoError(t, err)
err = c.Add("foo", valNode)
require.EqualError(t, err, "key foo already exists and can not be added")
Expand All @@ -131,7 +131,7 @@ foo:
Patch: func(t *testing.T, node *yaml.Node) {
c, err := newContainer(node)
require.NoError(t, err)
valNode, err := valueToYAMLNode("newvalue")
valNode, err := valueToYAMLNode("newvalue", "")
require.NoError(t, err)
err = c.Set("foo", valNode)
require.NoError(t, err)
Expand All @@ -144,7 +144,7 @@ foo:
Patch: func(t *testing.T, node *yaml.Node) {
c, err := newContainer(node)
require.NoError(t, err)
valNode, err := valueToYAMLNode(map[string]interface{}{"bar": "update", "baz": 2})
valNode, err := valueToYAMLNode(map[string]interface{}{"bar": "update", "baz": 2}, "")
require.NoError(t, err)
err = c.Set("foo", valNode)
require.NoError(t, err)
Expand All @@ -160,7 +160,7 @@ foo:
Patch: func(t *testing.T, node *yaml.Node) {
c, err := newContainer(node)
require.NoError(t, err)
valNode, err := valueToYAMLNode("newvalue")
valNode, err := valueToYAMLNode("newvalue", "")
require.NoError(t, err)
err = c.Set("notfound", valNode)
require.EqualError(t, err, "key notfound does not exist and can not be replaced")
Expand Down Expand Up @@ -291,7 +291,7 @@ foo:
Patch: func(t *testing.T, node *yaml.Node) {
c, err := newContainer(node)
require.NoError(t, err)
valNode, err := valueToYAMLNode("newvalue")
valNode, err := valueToYAMLNode("newvalue", "")
require.NoError(t, err)
err = c.Add("-", valNode)
require.NoError(t, err)
Expand All @@ -311,7 +311,7 @@ foo:
Patch: func(t *testing.T, node *yaml.Node) {
c, err := newContainer(node)
require.NoError(t, err)
valNode, err := valueToYAMLNode("newvalue")
valNode, err := valueToYAMLNode("newvalue", "")
require.NoError(t, err)
err = c.Add("3", valNode)
require.NoError(t, err)
Expand All @@ -331,7 +331,7 @@ foo:
Patch: func(t *testing.T, node *yaml.Node) {
c, err := newContainer(node)
require.NoError(t, err)
valNode, err := valueToYAMLNode("newvalue")
valNode, err := valueToYAMLNode("newvalue", "")
require.NoError(t, err)
err = c.Add("0", valNode)
require.NoError(t, err)
Expand All @@ -351,7 +351,7 @@ foo:
Patch: func(t *testing.T, node *yaml.Node) {
c, err := newContainer(node)
require.NoError(t, err)
valNode, err := valueToYAMLNode("newvalue")
valNode, err := valueToYAMLNode("newvalue", "")
require.NoError(t, err)
err = c.Add("1", valNode)
require.NoError(t, err)
Expand All @@ -368,7 +368,7 @@ foo:
Patch: func(t *testing.T, node *yaml.Node) {
c, err := newContainer(node)
require.NoError(t, err)
valNode, err := valueToYAMLNode("newvalue")
valNode, err := valueToYAMLNode("newvalue", "")
require.NoError(t, err)
err = c.Add("-", valNode)
require.NoError(t, err)
Expand All @@ -385,7 +385,7 @@ foo:
Patch: func(t *testing.T, node *yaml.Node) {
c, err := newContainer(node)
require.NoError(t, err)
valNode, err := valueToYAMLNode("newvalue")
valNode, err := valueToYAMLNode("newvalue", "")
require.NoError(t, err)
err = c.Add("4", valNode)
require.EqualError(t, err, "add index key out of bounds (idx 4, len 3)")
Expand All @@ -404,7 +404,7 @@ foo:
Patch: func(t *testing.T, node *yaml.Node) {
c, err := newContainer(node)
require.NoError(t, err)
valNode, err := valueToYAMLNode("newvalue")
valNode, err := valueToYAMLNode("newvalue", "")
require.NoError(t, err)
err = c.Set("1", valNode)
require.NoError(t, err)
Expand All @@ -423,7 +423,7 @@ foo:
Patch: func(t *testing.T, node *yaml.Node) {
c, err := newContainer(node)
require.NoError(t, err)
valNode, err := valueToYAMLNode(map[string]interface{}{"bar": "update", "baz": 2})
valNode, err := valueToYAMLNode(map[string]interface{}{"bar": "update", "baz": 2}, "")
require.NoError(t, err)
err = c.Set("1", valNode)
require.NoError(t, err)
Expand All @@ -442,7 +442,7 @@ foo:
Patch: func(t *testing.T, node *yaml.Node) {
c, err := newContainer(node)
require.NoError(t, err)
valNode, err := valueToYAMLNode("newvalue")
valNode, err := valueToYAMLNode("newvalue", "")
require.NoError(t, err)
err = c.Set("2", valNode)
require.EqualError(t, err, "set index key out of bounds (idx 2, len 2)")
Expand Down
20 changes: 11 additions & 9 deletions yamlpatch/patch.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,11 @@ func Apply(originalBytes []byte, patch Patch) ([]byte, error) {
var err error
switch op.Type {
case OperationAdd:
err = patchAdd(node, op.Path, op.Value)
err = patchAdd(node, op.Path, op.Value, op.Comment)
case OperationRemove:
err = patchRemove(node, op.Path)
case OperationReplace:
err = patchReplace(node, op.Path, op.Value)
err = patchReplace(node, op.Path, op.Value, op.Comment)
case OperationMove:
err = patchMove(node, op.Path, op.From)
case OperationCopy:
Expand All @@ -64,8 +64,8 @@ func Apply(originalBytes []byte, patch Patch) ([]byte, error) {
return buf.Bytes(), nil
}

func patchAdd(node *yaml.Node, path Path, value interface{}) error {
valueNode, err := valueToYAMLNode(value)
func patchAdd(node *yaml.Node, path Path, value interface{}, comment string) error {
valueNode, err := valueToYAMLNode(value, comment)
if err != nil {
return err
}
Expand Down Expand Up @@ -100,12 +100,12 @@ func patchRemove(node *yaml.Node, path Path) error {
return parent.Remove(path.Key())
}

func patchReplace(node *yaml.Node, path Path, value interface{}) error {
func patchReplace(node *yaml.Node, path Path, value interface{}, comment string) error {
parent, _, err := getParentAndLeaf(node, path)
if err != nil {
return err
}
valueNode, err := valueToYAMLNode(value)
valueNode, err := valueToYAMLNode(value, comment)
if err != nil {
return err
}
Expand Down Expand Up @@ -169,8 +169,9 @@ func patchTest(node *yaml.Node, path Path, testValue interface{}) error {
if err != nil {
return err
}
// roundtrip test value to use standard type
testValueNode, err := valueToYAMLNode(testValue)
// roundtrip test value to use standard type, comment is unset since this operation only
// looks at the existing value and compares it against the test value
testValueNode, err := valueToYAMLNode(testValue, "")
if err != nil {
return err
}
Expand Down Expand Up @@ -222,7 +223,7 @@ func unmarshalNode(text []byte) (*yaml.Node, error) {
return node.Content[0], nil
}

func valueToYAMLNode(value interface{}) (*yaml.Node, error) {
func valueToYAMLNode(value interface{}, comment string) (*yaml.Node, error) {
yamlBytes, err := yaml.Marshal(value)
if err != nil {
return nil, err
Expand All @@ -232,6 +233,7 @@ func valueToYAMLNode(value interface{}) (*yaml.Node, error) {
return nil, err
}
clearYAMLStyle(node)
node.HeadComment = comment
return node, nil
}

Expand Down
32 changes: 32 additions & 0 deletions yamlpatch/patch_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,38 @@ foo:
foo:
arr: [0, 1, 2, 3]
bar: 1 # my bar`,
},
{
Name: "add to array with comment",
Patch: []string{`{"op":"add","path":"/foo/arr/-","value":4,"comment":"the number 4"}`},
Body: `# my foo
foo:
arr:
# numbers 1 through 3
- 1
- 2
- 3`,
Expected: `# my foo
foo:
arr:
# numbers 1 through 3
- 1
- 2
- 3
# the number 4
- 4`,
},
{
Name: "replace object with comment",
Patch: []string{`{"op":"replace","path":"/foo/bar","value":{"key":"value"},"comment":"key value pair"}`},
Body: `# my foo
foo:
bar: hello-world # previous comment`,
Expected: `# my foo
foo:
bar:
# key value pair
key: value`,
},
// Test cases from json-patch: https://github.com/evanphx/json-patch/blob/master/patch_test.go to verify JSON Patch correctness.
{
Expand Down
3 changes: 3 additions & 0 deletions yamlpatch/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ type Operation struct {
Path Path `json:"path" yaml:"path"`
From Path `json:"from,omitempty" yaml:"from,omitempty"`
Value interface{} `json:"value,omitempty" yaml:"value,omitempty"`

// If not empty, will be inserted as a head comment above this node
Comment string `json:"comment,omitempty" yaml:"comment,omitempty"`
}

func (op Operation) String() string {
Expand Down

0 comments on commit e59b3e8

Please sign in to comment.