mirror of
https://github.com/mikefarah/yq.git
synced 2024-11-12 05:38:04 +00:00
Added new "c" flag to clobber custom tags when needed
This commit is contained in:
parent
b1a40a9fb7
commit
b4ca184108
@ -157,7 +157,7 @@ func (n *CandidateNode) UpdateAttributesFrom(other *CandidateNode, prefs assignP
|
|||||||
n.Node.Kind = other.Node.Kind
|
n.Node.Kind = other.Node.Kind
|
||||||
|
|
||||||
// don't clobber custom tags...
|
// don't clobber custom tags...
|
||||||
if strings.HasPrefix(n.Node.Tag, "!!") || n.Node.Tag == "" {
|
if prefs.ClobberCustomTags || strings.HasPrefix(n.Node.Tag, "!!") || n.Node.Tag == "" {
|
||||||
n.Node.Tag = other.Node.Tag
|
n.Node.Tag = other.Node.Tag
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,6 +7,11 @@ Which will assign the LHS node values to the RHS node values. The RHS expression
|
|||||||
|
|
||||||
### relative form: `|=`
|
### relative form: `|=`
|
||||||
This will do a similar thing to the plain form, however, the RHS expression is run against _the LHS nodes_. This is useful for updating values based on old values, e.g. increment.
|
This will do a similar thing to the plain form, however, the RHS expression is run against _the LHS nodes_. This is useful for updating values based on old values, e.g. increment.
|
||||||
|
|
||||||
|
|
||||||
|
### Flags
|
||||||
|
- `c` clobber custom tags
|
||||||
|
|
||||||
{% hint style="warning" %}
|
{% hint style="warning" %}
|
||||||
Note that versions prior to 4.18 require the 'eval/e' command to be specified. 
|
Note that versions prior to 4.18 require the 'eval/e' command to be specified. 
|
||||||
|
|
||||||
@ -235,3 +240,37 @@ a:
|
|||||||
- bogs
|
- bogs
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Custom types are maintained by default
|
||||||
|
Given a sample.yml file of:
|
||||||
|
```yaml
|
||||||
|
a: !cat meow
|
||||||
|
b: !dog woof
|
||||||
|
```
|
||||||
|
then
|
||||||
|
```bash
|
||||||
|
yq '.a = .b' sample.yml
|
||||||
|
```
|
||||||
|
will output
|
||||||
|
```yaml
|
||||||
|
a: !cat woof
|
||||||
|
b: !dog woof
|
||||||
|
```
|
||||||
|
|
||||||
|
## Custom types: clovver
|
||||||
|
Use the `c` option to clobber custom tags
|
||||||
|
|
||||||
|
Given a sample.yml file of:
|
||||||
|
```yaml
|
||||||
|
a: !cat meow
|
||||||
|
b: !dog woof
|
||||||
|
```
|
||||||
|
then
|
||||||
|
```bash
|
||||||
|
yq '.a =c .b' sample.yml
|
||||||
|
```
|
||||||
|
will output
|
||||||
|
```yaml
|
||||||
|
a: !dog woof
|
||||||
|
b: !dog woof
|
||||||
|
```
|
||||||
|
|
||||||
|
@ -6,4 +6,8 @@ This operator is used to update node values. It can be used in either the:
|
|||||||
Which will assign the LHS node values to the RHS node values. The RHS expression is run against the matching nodes in the pipeline.
|
Which will assign the LHS node values to the RHS node values. The RHS expression is run against the matching nodes in the pipeline.
|
||||||
|
|
||||||
### relative form: `|=`
|
### relative form: `|=`
|
||||||
This will do a similar thing to the plain form, however, the RHS expression is run against _the LHS nodes_. This is useful for updating values based on old values, e.g. increment.
|
This will do a similar thing to the plain form, however, the RHS expression is run against _the LHS nodes_. This is useful for updating values based on old values, e.g. increment.
|
||||||
|
|
||||||
|
|
||||||
|
### Flags
|
||||||
|
- `c` clobber custom tags
|
||||||
|
@ -14,6 +14,7 @@ You can control how objects are merged by using one or more of the following fla
|
|||||||
- `d` deeply merge arrays
|
- `d` deeply merge arrays
|
||||||
- `?` only merge _existing_ fields
|
- `?` only merge _existing_ fields
|
||||||
- `n` only merge _new_ fields
|
- `n` only merge _new_ fields
|
||||||
|
- `c` clobber custom tags
|
||||||
|
|
||||||
|
|
||||||
### Merge two files together
|
### Merge two files together
|
||||||
|
@ -14,6 +14,7 @@ You can control how objects are merged by using one or more of the following fla
|
|||||||
- `d` deeply merge arrays
|
- `d` deeply merge arrays
|
||||||
- `?` only merge _existing_ fields
|
- `?` only merge _existing_ fields
|
||||||
- `n` only merge _new_ fields
|
- `n` only merge _new_ fields
|
||||||
|
- `c` clobber custom tags
|
||||||
|
|
||||||
|
|
||||||
### Merge two files together
|
### Merge two files together
|
||||||
@ -481,3 +482,26 @@ b: !goat
|
|||||||
dog: woof
|
dog: woof
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Custom types: clobber tags
|
||||||
|
Use the `c` option to clobber custom tags. Note that the second tag is now used
|
||||||
|
|
||||||
|
Given a sample.yml file of:
|
||||||
|
```yaml
|
||||||
|
a: !horse
|
||||||
|
cat: meow
|
||||||
|
b: !goat
|
||||||
|
dog: woof
|
||||||
|
```
|
||||||
|
then
|
||||||
|
```bash
|
||||||
|
yq '.a *=c .b' sample.yml
|
||||||
|
```
|
||||||
|
will output
|
||||||
|
```yaml
|
||||||
|
a: !goat
|
||||||
|
cat: meow
|
||||||
|
dog: woof
|
||||||
|
b: !goat
|
||||||
|
dog: woof
|
||||||
|
```
|
||||||
|
|
||||||
|
@ -182,8 +182,8 @@ var participleYqRules = []*participleYqRule{
|
|||||||
{"GreaterThan", `\s*>\s*`, opTokenWithPrefs(compareOpType, nil, compareTypePref{OrEqual: false, Greater: true}), 0},
|
{"GreaterThan", `\s*>\s*`, opTokenWithPrefs(compareOpType, nil, compareTypePref{OrEqual: false, Greater: true}), 0},
|
||||||
{"LessThan", `\s*<\s*`, opTokenWithPrefs(compareOpType, nil, compareTypePref{OrEqual: false, Greater: false}), 0},
|
{"LessThan", `\s*<\s*`, opTokenWithPrefs(compareOpType, nil, compareTypePref{OrEqual: false, Greater: false}), 0},
|
||||||
|
|
||||||
{"AssignRelative", `\|=`, assignOpToken(true), 0},
|
{"AssignRelative", `\|=[c]*`, assignOpToken(true), 0},
|
||||||
{"Assign", `=`, assignOpToken(false), 0},
|
{"Assign", `=[c]*`, assignOpToken(false), 0},
|
||||||
|
|
||||||
{`whitespace`, `[ \t\n]+`, nil, 0},
|
{`whitespace`, `[ \t\n]+`, nil, 0},
|
||||||
|
|
||||||
@ -194,8 +194,8 @@ var participleYqRules = []*participleYqRule{
|
|||||||
|
|
||||||
{"Union", `,`, opToken(unionOpType), 0},
|
{"Union", `,`, opToken(unionOpType), 0},
|
||||||
|
|
||||||
{"MultiplyAssign", `\*=[\+|\?dn]*`, multiplyWithPrefs(multiplyAssignOpType), 0},
|
{"MultiplyAssign", `\*=[\+|\?cdn]*`, multiplyWithPrefs(multiplyAssignOpType), 0},
|
||||||
{"Multiply", `\*[\+|\?dn]*`, multiplyWithPrefs(multiplyOpType), 0},
|
{"Multiply", `\*[\+|\?cdn]*`, multiplyWithPrefs(multiplyOpType), 0},
|
||||||
|
|
||||||
{"AddAssign", `\+=`, opToken(addAssignOpType), 0},
|
{"AddAssign", `\+=`, opToken(addAssignOpType), 0},
|
||||||
{"Add", `\+`, opToken(addOpType), 0},
|
{"Add", `\+`, opToken(addOpType), 0},
|
||||||
@ -317,6 +317,9 @@ func assignOpToken(updateAssign bool) yqAction {
|
|||||||
log.Debug("assignOpToken %v", rawToken.Value)
|
log.Debug("assignOpToken %v", rawToken.Value)
|
||||||
value := rawToken.Value
|
value := rawToken.Value
|
||||||
prefs := assignPreferences{DontOverWriteAnchor: true}
|
prefs := assignPreferences{DontOverWriteAnchor: true}
|
||||||
|
if strings.Contains(value, "c") {
|
||||||
|
prefs.ClobberCustomTags = true
|
||||||
|
}
|
||||||
op := &Operation{OperationType: assignOpType, Value: assignOpType.Type, StringValue: value, UpdateAssign: updateAssign, Preferences: prefs}
|
op := &Operation{OperationType: assignOpType, Value: assignOpType.Type, StringValue: value, UpdateAssign: updateAssign, Preferences: prefs}
|
||||||
return &token{TokenType: operationToken, Operation: op}, nil
|
return &token{TokenType: operationToken, Operation: op}, nil
|
||||||
}
|
}
|
||||||
@ -387,6 +390,7 @@ func envSubstWithOptions() yqAction {
|
|||||||
func multiplyWithPrefs(op *operationType) yqAction {
|
func multiplyWithPrefs(op *operationType) yqAction {
|
||||||
return func(rawToken lexer.Token) (*token, error) {
|
return func(rawToken lexer.Token) (*token, error) {
|
||||||
prefs := multiplyPreferences{}
|
prefs := multiplyPreferences{}
|
||||||
|
prefs.AssignPrefs = assignPreferences{}
|
||||||
options := rawToken.Value
|
options := rawToken.Value
|
||||||
if strings.Contains(options, "+") {
|
if strings.Contains(options, "+") {
|
||||||
prefs.AppendArrays = true
|
prefs.AppendArrays = true
|
||||||
@ -395,11 +399,14 @@ func multiplyWithPrefs(op *operationType) yqAction {
|
|||||||
prefs.TraversePrefs = traversePreferences{DontAutoCreate: true}
|
prefs.TraversePrefs = traversePreferences{DontAutoCreate: true}
|
||||||
}
|
}
|
||||||
if strings.Contains(options, "n") {
|
if strings.Contains(options, "n") {
|
||||||
prefs.AssignPrefs = assignPreferences{OnlyWriteNull: true}
|
prefs.AssignPrefs.OnlyWriteNull = true
|
||||||
}
|
}
|
||||||
if strings.Contains(options, "d") {
|
if strings.Contains(options, "d") {
|
||||||
prefs.DeepMergeArrays = true
|
prefs.DeepMergeArrays = true
|
||||||
}
|
}
|
||||||
|
if strings.Contains(options, "c") {
|
||||||
|
prefs.AssignPrefs.ClobberCustomTags = true
|
||||||
|
}
|
||||||
prefs.TraversePrefs.DontFollowAlias = true
|
prefs.TraversePrefs.DontFollowAlias = true
|
||||||
op := &Operation{OperationType: op, Value: multiplyOpType.Type, StringValue: options, Preferences: prefs}
|
op := &Operation{OperationType: op, Value: multiplyOpType.Type, StringValue: options, Preferences: prefs}
|
||||||
return &token{TokenType: operationToken, Operation: op}, nil
|
return &token{TokenType: operationToken, Operation: op}, nil
|
||||||
|
@ -3,6 +3,7 @@ package yqlib
|
|||||||
type assignPreferences struct {
|
type assignPreferences struct {
|
||||||
DontOverWriteAnchor bool
|
DontOverWriteAnchor bool
|
||||||
OnlyWriteNull bool
|
OnlyWriteNull bool
|
||||||
|
ClobberCustomTags bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func assignUpdateFunc(prefs assignPreferences) crossFunctionCalculation {
|
func assignUpdateFunc(prefs assignPreferences) crossFunctionCalculation {
|
||||||
@ -15,18 +16,29 @@ func assignUpdateFunc(prefs assignPreferences) crossFunctionCalculation {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// they way *= (multipleAssign) is handled, we set the multiplePrefs
|
||||||
|
// on the node, not assignPrefs. Long story.
|
||||||
|
func getAssignPreferences(preferences interface{}) assignPreferences {
|
||||||
|
prefs := assignPreferences{}
|
||||||
|
|
||||||
|
switch typedPref := preferences.(type) {
|
||||||
|
case assignPreferences:
|
||||||
|
prefs = typedPref
|
||||||
|
case multiplyPreferences:
|
||||||
|
prefs = typedPref.AssignPrefs
|
||||||
|
}
|
||||||
|
return prefs
|
||||||
|
}
|
||||||
|
|
||||||
func assignUpdateOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
|
func assignUpdateOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
|
||||||
lhs, err := d.GetMatchingNodes(context, expressionNode.LHS)
|
lhs, err := d.GetMatchingNodes(context, expressionNode.LHS)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return Context{}, err
|
return Context{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
prefs := assignPreferences{}
|
prefs := getAssignPreferences(expressionNode.Operation.Preferences)
|
||||||
// they way *= (multipleAssign) is handled, we set the multiplePrefs
|
|
||||||
// on the node, not assignPrefs. Long story.
|
log.Debug("assignUpdateOperator prefs: %v", prefs)
|
||||||
if p, ok := expressionNode.Operation.Preferences.(assignPreferences); ok {
|
|
||||||
prefs = p
|
|
||||||
}
|
|
||||||
|
|
||||||
if !expressionNode.Operation.UpdateAssign {
|
if !expressionNode.Operation.UpdateAssign {
|
||||||
// this works because we already ran against LHS with an editable context.
|
// this works because we already ran against LHS with an editable context.
|
||||||
|
@ -208,6 +208,23 @@ var assignOperatorScenarios = []expressionScenario{
|
|||||||
"D0, P[], (doc)::a:\n b:\n - null\n - c: bogs\n",
|
"D0, P[], (doc)::a:\n b:\n - null\n - c: bogs\n",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
description: "Custom types are maintained by default",
|
||||||
|
document: "a: !cat meow\nb: !dog woof",
|
||||||
|
expression: `.a = .b`,
|
||||||
|
expected: []string{
|
||||||
|
"D0, P[], (doc)::a: !cat woof\nb: !dog woof\n",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: "Custom types: clovver",
|
||||||
|
subdescription: "Use the `c` option to clobber custom tags",
|
||||||
|
document: "a: !cat meow\nb: !dog woof",
|
||||||
|
expression: `.a =c .b`,
|
||||||
|
expected: []string{
|
||||||
|
"D0, P[], (doc)::a: !dog woof\nb: !dog woof\n",
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAssignOperatorScenarios(t *testing.T) {
|
func TestAssignOperatorScenarios(t *testing.T) {
|
||||||
|
@ -523,6 +523,35 @@ var multiplyOperatorScenarios = []expressionScenario{
|
|||||||
"D0, P[], (doc)::a: !horse {cat: meow, dog: woof}\nb: !goat {dog: woof}\n",
|
"D0, P[], (doc)::a: !horse {cat: meow, dog: woof}\nb: !goat {dog: woof}\n",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
description: "Custom types: clobber tags",
|
||||||
|
subdescription: "Use the `c` option to clobber custom tags. Note that the second tag is now used",
|
||||||
|
document: "a: !horse {cat: meow}\nb: !goat {dog: woof}",
|
||||||
|
expression: ".a *=c .b",
|
||||||
|
expected: []string{
|
||||||
|
"D0, P[], (doc)::a: !goat {cat: meow, dog: woof}\nb: !goat {dog: woof}\n",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
skipDoc: true,
|
||||||
|
description: "Custom types: clobber tags - *=",
|
||||||
|
subdescription: "Use the `c` option to clobber custom tags - on both the `=` and `*` operator. Note that the second tag is now used",
|
||||||
|
document: "a: !horse {cat: meow}\nb: !goat {dog: woof}",
|
||||||
|
expression: ".a =c .a *c .b",
|
||||||
|
expected: []string{
|
||||||
|
"D0, P[], (doc)::a: !goat {cat: meow, dog: woof}\nb: !goat {dog: woof}\n",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
skipDoc: true,
|
||||||
|
description: "Custom types: dont clobber tags - *=",
|
||||||
|
subdescription: "Use the `c` option to clobber custom tags - on both the `=` and `*` operator. Note that the second tag is now used",
|
||||||
|
document: "a: !horse {cat: meow}\nb: !goat {dog: woof}",
|
||||||
|
expression: ".a *= .b",
|
||||||
|
expected: []string{
|
||||||
|
"D0, P[], (doc)::a: !horse {cat: meow, dog: woof}\nb: !goat {dog: woof}\n",
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
skipDoc: true,
|
skipDoc: true,
|
||||||
description: "Custom types: that are really maps",
|
description: "Custom types: that are really maps",
|
||||||
|
@ -5,6 +5,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/jinzhu/copier"
|
"github.com/jinzhu/copier"
|
||||||
|
logging "gopkg.in/op/go-logging.v1"
|
||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -83,8 +84,10 @@ func resultsForRHS(d *dataTreeNavigator, context Context, lhsCandidate *Candidat
|
|||||||
}
|
}
|
||||||
|
|
||||||
for rightEl := rhs.MatchingNodes.Front(); rightEl != nil; rightEl = rightEl.Next() {
|
for rightEl := rhs.MatchingNodes.Front(); rightEl != nil; rightEl = rightEl.Next() {
|
||||||
log.Debugf("Applying calc")
|
|
||||||
rhsCandidate := rightEl.Value.(*CandidateNode)
|
rhsCandidate := rightEl.Value.(*CandidateNode)
|
||||||
|
if !log.IsEnabledFor(logging.DEBUG) {
|
||||||
|
log.Debugf("Applying lhs: %v, rhsCandidate, %v", NodeToString(lhsCandidate), NodeToString(rhsCandidate))
|
||||||
|
}
|
||||||
resultCandidate, err := prefs.Calculation(d, context, lhsCandidate, rhsCandidate)
|
resultCandidate, err := prefs.Calculation(d, context, lhsCandidate, rhsCandidate)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -1,3 +1,8 @@
|
|||||||
|
4.27.3:
|
||||||
|
- Added new 'c' merge and assign flag that clobbers custom tags
|
||||||
|
- Bumped go dependency to fix CVE (#1316)
|
||||||
|
- Updated dependencies
|
||||||
|
|
||||||
4.27.2:
|
4.27.2:
|
||||||
- Fixed JSON decoder to maintain object key order.
|
- Fixed JSON decoder to maintain object key order.
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user