Skip to content

Commit 4478bd1

Browse files
committed
Fixed loading yaml with header issue #1445
1 parent 91dc315 commit 4478bd1

File tree

6 files changed

+51
-8
lines changed

6 files changed

+51
-8
lines changed

examples/small.yaml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
---
2+
# comment
3+
# about things
4+
a: cat

pkg/yqlib/decoder_yaml.go

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package yqlib
22

33
import (
44
"bufio"
5+
"bytes"
56
"errors"
67
"io"
78
"regexp"
@@ -12,11 +13,15 @@ import (
1213

1314
type yamlDecoder struct {
1415
decoder yaml.Decoder
16+
17+
prefs YamlPreferences
18+
1519
// work around of various parsing issues by yaml.v3 with document headers
16-
prefs YamlPreferences
1720
leadingContent string
18-
readAnything bool
19-
firstFile bool
21+
bufferRead bytes.Buffer
22+
23+
readAnything bool
24+
firstFile bool
2025
}
2126

2227
func NewYamlDecoder(prefs YamlPreferences) Decoder {
@@ -59,6 +64,7 @@ func (dec *yamlDecoder) processReadStream(reader *bufio.Reader) (io.Reader, stri
5964
func (dec *yamlDecoder) Init(reader io.Reader) error {
6065
readerToUse := reader
6166
leadingContent := ""
67+
dec.bufferRead = bytes.Buffer{}
6268
var err error
6369
// if we 'evaluating together' - we only process the leading content
6470
// of the first file - this ensures comments from subsequent files are
@@ -68,6 +74,12 @@ func (dec *yamlDecoder) Init(reader io.Reader) error {
6874
if err != nil {
6975
return err
7076
}
77+
} else if !dec.prefs.LeadingContentPreProcessing {
78+
// if we're not process the leading content
79+
// keep a copy of what we've read. This is incase its a
80+
// doc with only comments - the decoder will return nothing
81+
// then we can read the comments from bufferRead
82+
readerToUse = io.TeeReader(reader, &dec.bufferRead)
7183
}
7284
dec.leadingContent = leadingContent
7385
dec.readAnything = false
@@ -78,13 +90,20 @@ func (dec *yamlDecoder) Init(reader io.Reader) error {
7890

7991
func (dec *yamlDecoder) Decode() (*CandidateNode, error) {
8092
var dataBucket yaml.Node
81-
8293
err := dec.decoder.Decode(&dataBucket)
8394
if errors.Is(err, io.EOF) && dec.leadingContent != "" && !dec.readAnything {
8495
// force returning an empty node with a comment.
8596
dec.readAnything = true
8697
return dec.blankNodeWithComment(), nil
87-
98+
} else if errors.Is(err, io.EOF) && !dec.prefs.LeadingContentPreProcessing && !dec.readAnything {
99+
// didn't find any yaml,
100+
// check the tee buffer, maybe there were comments
101+
dec.readAnything = true
102+
dec.leadingContent = dec.bufferRead.String()
103+
if dec.leadingContent != "" {
104+
return dec.blankNodeWithComment(), nil
105+
}
106+
return nil, err
88107
} else if err != nil {
89108
return nil, err
90109
}
@@ -97,6 +116,7 @@ func (dec *yamlDecoder) Decode() (*CandidateNode, error) {
97116
candidateNode.LeadingContent = dec.leadingContent
98117
dec.leadingContent = ""
99118
}
119+
dec.readAnything = true
100120
// move document comments into candidate node
101121
// otherwise unwrap drops them.
102122
candidateNode.TrailingContent = dataBucket.FootComment

pkg/yqlib/lexer_participle.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ var participleYqRules = []*participleYqRule{
8686

8787
{"LoadString", `load_?str|str_?load`, loadOp(nil, true), 0},
8888

89-
{"LoadYaml", `load`, loadOp(NewYamlDecoder(ConfiguredYamlPreferences), false), 0},
89+
{"LoadYaml", `load`, loadOp(NewYamlDecoder(LoadYamlPreferences), false), 0},
9090

9191
{"SplitDocument", `splitDoc|split_?doc`, opToken(splitDocumentOpType), 0},
9292

pkg/yqlib/operator_load.go

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,13 @@ import (
99
"gopkg.in/yaml.v3"
1010
)
1111

12+
var LoadYamlPreferences = YamlPreferences{
13+
LeadingContentPreProcessing: false,
14+
PrintDocSeparators: true,
15+
UnwrapScalar: true,
16+
EvaluateTogether: false,
17+
}
18+
1219
type loadPrefs struct {
1320
loadAsString bool
1421
decoder Decoder
@@ -43,7 +50,10 @@ func loadYaml(filename string, decoder Decoder) (*CandidateNode, error) {
4350
// return null candidate
4451
return &CandidateNode{Node: &yaml.Node{Kind: yaml.ScalarNode, Tag: "!!null"}}, nil
4552
} else if documents.Len() == 1 {
46-
return documents.Front().Value.(*CandidateNode), nil
53+
candidate := documents.Front().Value.(*CandidateNode)
54+
log.Debug("first comment:", candidate.LeadingContent)
55+
// candidate.Node.Content[0].Content[0].HeadComment = candidate.LeadingContent
56+
return candidate, nil
4757

4858
} else {
4959
sequenceNode := &CandidateNode{Node: &yaml.Node{Kind: yaml.SequenceNode}}

pkg/yqlib/operator_load_test.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,15 @@ var loadScenarios = []expressionScenario{
1313
"D0, P[], (doc)::# comment\n\n",
1414
},
1515
},
16+
{
17+
skipDoc: true,
18+
description: "Load file with a header comment into an array",
19+
document: `- "../../examples/small.yaml"`,
20+
expression: `.[] |= load(.)`,
21+
expected: []string{
22+
"D0, P[], (doc)::- # comment\n # about things\n a: cat\n",
23+
},
24+
},
1625
{
1726
skipDoc: true,
1827
description: "Load empty file with no comment",

pkg/yqlib/operators_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ type expressionScenario struct {
3131
}
3232

3333
func TestMain(m *testing.M) {
34-
logging.SetLevel(logging.ERROR, "")
34+
logging.SetLevel(logging.DEBUG, "")
3535
Now = func() time.Time {
3636
return time.Date(2021, time.May, 19, 1, 2, 3, 4, time.UTC)
3737
}

0 commit comments

Comments
 (0)