Skip to content

Commit 910257c

Browse files
committed
Add ParseAll
1 parent 0657e63 commit 910257c

File tree

3 files changed

+99
-2
lines changed

3 files changed

+99
-2
lines changed

go.mod

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@ module github.com/buildkite/go-pipeline
22

33
go 1.23.0
44

5-
toolchain go1.24.1
6-
75
retract (
86
v1.0.1 // Solely to publish the retraction of v1.0.0. We'll skip straight to v1.0.2 when we're ready to publish
97
v1.0.0 // Published accidentally.

parser.go

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package pipeline
33
import (
44
"errors"
55
"io"
6+
"iter"
67
"strings"
78

89
"github.com/buildkite/go-pipeline/ordered"
@@ -13,6 +14,9 @@ import (
1314
type Options func(*Pipeline)
1415

1516
// Parse parses a pipeline. It does not apply interpolation.
17+
// Note that it only parses the first YAML document in the input.
18+
// To parse all YAML documents in the input reader as pipelines, use ParseAll.
19+
//
1620
// Warnings are passed through the err return:
1721
//
1822
// p, err := Parse(src)
@@ -42,6 +46,46 @@ func Parse(src io.Reader) (*Pipeline, error) {
4246
return p, ordered.Unmarshal(n, p)
4347
}
4448

49+
// ParseAll parses all YAML documents in the input stream as pipelines.
50+
// It does not stop yielding on decoding or unmarshaling errors.
51+
// It does not apply interpolation.
52+
//
53+
// Warnings are passed through the err return:
54+
//
55+
// for p, err := range ParseAll(src) {
56+
// if w := warning.As(err); w != nil {
57+
// // Here are some warnings that should be shown
58+
// log.Printf("*Warning* - pipeline is not fully parsed:\n%v", w)
59+
// } else if err != nil {
60+
// // Parse could not understand src at all
61+
// return err
62+
// }
63+
// // Use p
64+
// }
65+
func ParseAll(src io.Reader) iter.Seq2[*Pipeline, error] {
66+
dec := yaml.NewDecoder(src)
67+
return func(yield func(*Pipeline, error) bool) {
68+
for {
69+
// The loop body here is essentially similar to Parse (above).
70+
n := new(yaml.Node)
71+
if err := dec.Decode(n); err != nil {
72+
if err == io.EOF {
73+
return // end of stream
74+
}
75+
if !yield(nil, formatYAMLError(err)) {
76+
return
77+
}
78+
continue
79+
}
80+
81+
p := new(Pipeline)
82+
if !yield(p, ordered.Unmarshal(n, p)) {
83+
return
84+
}
85+
}
86+
}
87+
}
88+
4589
func formatYAMLError(err error) error {
4690
return errors.New(strings.TrimPrefix(err.Error(), "yaml: "))
4791
}

parser_test.go

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,61 @@ func TestParserParsesYAML(t *testing.T) {
7070
}
7171
}
7272

73+
func TestParseAll_ParsesMultipleDocuments(t *testing.T) {
74+
input := strings.NewReader(`---
75+
steps:
76+
- command: "hallo"
77+
---
78+
steps:
79+
- command: "tschüss"
80+
---
81+
steps:
82+
- command: "bis später"
83+
`)
84+
85+
want := []*Pipeline{
86+
{Steps: Steps{&CommandStep{Command: "hallo"}}},
87+
{Steps: Steps{&CommandStep{Command: "tschüss"}}},
88+
{Steps: Steps{&CommandStep{Command: "bis später"}}},
89+
}
90+
91+
var got []*Pipeline
92+
93+
for gotPipeline, err := range ParseAll(input) {
94+
if err != nil {
95+
t.Fatalf("Parse(input) error = %v", err)
96+
}
97+
98+
got = append(got, gotPipeline)
99+
}
100+
101+
if diff := cmp.Diff(got, want, cmp.Comparer(ordered.EqualSS), cmp.Comparer(ordered.EqualSA)); diff != "" {
102+
t.Errorf("parsed pipelines diff (-got, +want):\n%s", diff)
103+
}
104+
105+
// Multiple objects can be encoded as separate documents with yaml.Encoder.
106+
var gotYAML strings.Builder
107+
enc := yaml.NewEncoder(&gotYAML)
108+
for i, p := range got {
109+
if err := enc.Encode(p); err != nil {
110+
t.Errorf("yaml.Marshal(got[%d]) error = %v", i, err)
111+
}
112+
}
113+
114+
wantYAML := `steps:
115+
- command: hallo
116+
---
117+
steps:
118+
- command: tschüss
119+
---
120+
steps:
121+
- command: bis später
122+
`
123+
if diff := cmp.Diff(gotYAML.String(), wantYAML); diff != "" {
124+
t.Errorf("marshalled YAML diff (-got +want):\n%s", diff)
125+
}
126+
}
127+
73128
func TestParserParsesYAMLWithInterpolation(t *testing.T) {
74129
tests := []struct {
75130
desc string

0 commit comments

Comments
 (0)