From 6e8cc00030b8165f159d72cc39ccfd67a97c483d Mon Sep 17 00:00:00 2001 From: Mike Farah Date: Mon, 14 Jul 2025 16:26:26 +1000 Subject: [PATCH] Added flag to fix #2110 --- cmd/root.go | 1 + pkg/yqlib/operator_anchors_aliases.go | 5 +- pkg/yqlib/operator_anchors_aliases_test.go | 55 ++++++++++++++++++++++ pkg/yqlib/yaml.go | 3 ++ 4 files changed, 63 insertions(+), 1 deletion(-) diff --git a/cmd/root.go b/cmd/root.go index b7360d3c..2071e278 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -196,6 +196,7 @@ yq -P -oy sample.json panic(err) } rootCmd.PersistentFlags().BoolVarP(&yqlib.ConfiguredYamlPreferences.LeadingContentPreProcessing, "header-preprocess", "", true, "Slurp any header comments and separators before processing expression.") + rootCmd.PersistentFlags().BoolVarP(&yqlib.ConfiguredYamlPreferences.FixMergeAnchorToSpec, "yaml-fix-merge-anchor-to-spec", "", false, "Fix merge anchor to match YAML spec. Will default to true in late 2025") rootCmd.PersistentFlags().StringVarP(&splitFileExp, "split-exp", "s", "", "print each result (or doc) into a file named (exp). [exp] argument must return a string. You can use $index in the expression as the result counter. The necessary directories will be created.") if err = rootCmd.RegisterFlagCompletionFunc("split-exp", cobra.NoFileCompletions); err != nil { diff --git a/pkg/yqlib/operator_anchors_aliases.go b/pkg/yqlib/operator_anchors_aliases.go index 4a2cdf4e..50744871 100644 --- a/pkg/yqlib/operator_anchors_aliases.go +++ b/pkg/yqlib/operator_anchors_aliases.go @@ -272,7 +272,10 @@ func overrideEntry(node *CandidateNode, key *CandidateNode, value *CandidateNode log.Debugf("checking new content %v:%v", keyNode.Value, valueEl.Value.(*CandidateNode).Value) if keyNode.Value == key.Value && keyNode.Alias == nil && key.Alias == nil { log.Debugf("overridign new content") - valueEl.Value = value + if !ConfiguredYamlPreferences.FixMergeAnchorToSpec { + log.Warning("--yaml-fix-merge-anchor-to-spec is false; causing the merge anchor to override the existing value at %v which isn't to the yaml spec. This flag will default to true in late 2025.", keyNode.GetNicePath()) + valueEl.Value = value + } return nil } newEl = valueEl // move forward twice diff --git a/pkg/yqlib/operator_anchors_aliases_test.go b/pkg/yqlib/operator_anchors_aliases_test.go index a24f3fdd..c5b2f74b 100644 --- a/pkg/yqlib/operator_anchors_aliases_test.go +++ b/pkg/yqlib/operator_anchors_aliases_test.go @@ -53,7 +53,54 @@ foobar: thing: foobar_thing ` +var explodeWhenKeysExistDocument = `objects: + - &circle + name: circle + shape: round + - name: ellipse + !!merge <<: *circle + - !!merge <<: *circle + name: egg +` + +var explodeWhenKeysExistLegacy = `D0, P[], (!!map)::objects: + - name: circle + shape: round + - name: circle + shape: round + - shape: round + name: egg +` + +var explodeWhenKeysExistExpected = `D0, P[], (!!map)::objects: + - name: circle + shape: round + - name: ellipse + shape: round + - shape: round + name: egg +` + +var fixedAnchorOperatorScenarios = []expressionScenario{ + { + skipDoc: true, + description: "merge anchor after existing keys", + subdescription: "legacy: overrides existing keys", + document: explodeWhenKeysExistDocument, + expression: "explode(.)", + expected: []string{explodeWhenKeysExistExpected}, + }, +} + var anchorOperatorScenarios = []expressionScenario{ + { + skipDoc: true, + description: "merge anchor after existing keys", + subdescription: "legacy: overrides existing keys", + document: explodeWhenKeysExistDocument, + expression: "explode(.)", + expected: []string{explodeWhenKeysExistLegacy}, + }, { skipDoc: true, description: "merge anchor not map", @@ -291,3 +338,11 @@ func TestAnchorAliasOperatorScenarios(t *testing.T) { } documentOperatorScenarios(t, "anchor-and-alias-operators", anchorOperatorScenarios) } + +func TestAnchorAliasOperatorAlignedToSpecScenarios(t *testing.T) { + ConfiguredYamlPreferences.FixMergeAnchorToSpec = true + for _, tt := range fixedAnchorOperatorScenarios { + testScenario(t, &tt) + } + ConfiguredYamlPreferences.FixMergeAnchorToSpec = false +} diff --git a/pkg/yqlib/yaml.go b/pkg/yqlib/yaml.go index 079bc749..1daba35d 100644 --- a/pkg/yqlib/yaml.go +++ b/pkg/yqlib/yaml.go @@ -7,6 +7,7 @@ type YamlPreferences struct { PrintDocSeparators bool UnwrapScalar bool EvaluateTogether bool + FixMergeAnchorToSpec bool } func NewDefaultYamlPreferences() YamlPreferences { @@ -17,6 +18,7 @@ func NewDefaultYamlPreferences() YamlPreferences { PrintDocSeparators: true, UnwrapScalar: true, EvaluateTogether: false, + FixMergeAnchorToSpec: false, } } @@ -28,6 +30,7 @@ func (p *YamlPreferences) Copy() YamlPreferences { PrintDocSeparators: p.PrintDocSeparators, UnwrapScalar: p.UnwrapScalar, EvaluateTogether: p.EvaluateTogether, + FixMergeAnchorToSpec: p.FixMergeAnchorToSpec, } }