1717package loader
1818
1919import (
20+ "bytes"
2021 "context"
2122 "fmt"
23+ "io"
2224 "os"
2325 paths "path"
2426 "path/filepath"
@@ -166,7 +168,9 @@ func WithProfiles(profiles []string) func(*Options) {
166168// ParseYAML reads the bytes from a file, parses the bytes into a mapping
167169// structure, and returns it.
168170func ParseYAML (source []byte ) (map [string ]interface {}, error ) {
169- m , _ , err := parseYAML (source )
171+ r := bytes .NewReader (source )
172+ decoder := yaml .NewDecoder (r )
173+ m , _ , err := parseYAML (decoder )
170174 return m , err
171175}
172176
@@ -179,11 +183,11 @@ type PostProcessor interface {
179183 Apply (config * types.Config ) error
180184}
181185
182- func parseYAML (source [] byte ) (map [string ]interface {}, PostProcessor , error ) {
186+ func parseYAML (decoder * yaml. Decoder ) (map [string ]interface {}, PostProcessor , error ) {
183187 var cfg interface {}
184188 processor := ResetProcessor {target : & cfg }
185189
186- if err := yaml . Unmarshal ( source , & processor ); err != nil {
190+ if err := decoder . Decode ( & processor ); err != nil {
187191 return nil , nil , err
188192 }
189193 stringMap , ok := cfg .(map [string ]interface {})
@@ -251,67 +255,86 @@ func load(ctx context.Context, configDetails types.ConfigDetails, opts *Options,
251255 loaded = append (loaded , mainFile )
252256
253257 includeRefs := make (map [string ][]types.IncludeConfig )
254- for i , file := range configDetails .ConfigFiles {
258+ first := true
259+ for _ , file := range configDetails .ConfigFiles {
255260 var postProcessor PostProcessor
256261 configDict := file .Config
257- if configDict == nil {
258- if len ( file . Content ) == 0 {
259- content , err := os . ReadFile ( file . Filename )
260- if err != nil {
261- return nil , err
262+
263+ processYaml := func () error {
264+ if ! opts . SkipValidation {
265+ if err := schema . Validate ( configDict ); err != nil {
266+ return fmt . Errorf ( "validating %s: %w" , file . Filename , err )
262267 }
263- file .Content = content
264268 }
265- dict , p , err := parseConfig (file .Content , opts )
269+
270+ configDict = groupXFieldsIntoExtensions (configDict )
271+
272+ cfg , err := loadSections (ctx , file .Filename , configDict , configDetails , opts )
266273 if err != nil {
267- return nil , fmt . Errorf ( "parsing %s: %w" , file . Filename , err )
274+ return err
268275 }
269- configDict = dict
270- file .Config = dict
271- configDetails .ConfigFiles [i ] = file
272- postProcessor = p
273- }
274276
275- if ! opts .SkipValidation {
276- if err := schema .Validate (configDict ); err != nil {
277- return nil , fmt .Errorf ("validating %s: %w" , file .Filename , err )
277+ if ! opts .SkipInclude {
278+ var included map [string ][]types.IncludeConfig
279+ cfg , included , err = loadInclude (ctx , file .Filename , configDetails , cfg , opts , loaded )
280+ if err != nil {
281+ return err
282+ }
283+ for k , v := range included {
284+ includeRefs [k ] = append (includeRefs [k ], v ... )
285+ }
278286 }
279- }
280-
281- configDict = groupXFieldsIntoExtensions (configDict )
282-
283- cfg , err := loadSections (ctx , file .Filename , configDict , configDetails , opts )
284- if err != nil {
285- return nil , err
286- }
287287
288- if ! opts .SkipInclude {
289- var included map [string ][]types.IncludeConfig
290- cfg , included , err = loadInclude (ctx , file .Filename , configDetails , cfg , opts , loaded )
288+ if first {
289+ first = false
290+ model = cfg
291+ return nil
292+ }
293+ merged , err := merge ([]* types.Config {model , cfg })
291294 if err != nil {
292- return nil , err
295+ return err
293296 }
294- for k , v := range included {
295- includeRefs [k ] = append (includeRefs [k ], v ... )
297+ if postProcessor != nil {
298+ err = postProcessor .Apply (merged )
299+ if err != nil {
300+ return err
301+ }
296302 }
303+ model = merged
304+ return nil
297305 }
298306
299- if i == 0 {
300- model = cfg
301- continue
302- }
307+ if configDict == nil {
308+ if len (file .Content ) == 0 {
309+ content , err := os .ReadFile (file .Filename )
310+ if err != nil {
311+ return nil , err
312+ }
313+ file .Content = content
314+ }
303315
304- merged , err := merge ([]* types.Config {model , cfg })
305- if err != nil {
306- return nil , err
307- }
308- if postProcessor != nil {
309- err = postProcessor .Apply (merged )
310- if err != nil {
316+ r := bytes .NewReader (file .Content )
317+ decoder := yaml .NewDecoder (r )
318+ for {
319+ dict , p , err := parseConfig (decoder , opts )
320+ if err != nil {
321+ if err != io .EOF {
322+ return nil , fmt .Errorf ("parsing %s: %w" , file .Filename , err )
323+ }
324+ break
325+ }
326+ configDict = dict
327+ postProcessor = p
328+
329+ if err := processYaml (); err != nil {
330+ return nil , err
331+ }
332+ }
333+ } else {
334+ if err := processYaml (); err != nil {
311335 return nil , err
312336 }
313337 }
314- model = merged
315338 }
316339
317340 project := & types.Project {
@@ -446,8 +469,8 @@ func NormalizeProjectName(s string) string {
446469 return strings .TrimLeft (s , "_-" )
447470}
448471
449- func parseConfig (b [] byte , opts * Options ) (map [string ]interface {}, PostProcessor , error ) {
450- yml , postProcessor , err := parseYAML (b )
472+ func parseConfig (decoder * yaml. Decoder , opts * Options ) (map [string ]interface {}, PostProcessor , error ) {
473+ yml , postProcessor , err := parseYAML (decoder )
451474 if err != nil {
452475 return nil , nil , err
453476 }
@@ -754,7 +777,10 @@ func loadServiceWithExtends(ctx context.Context, filename, name string, services
754777 return nil , err
755778 }
756779
757- baseFile , _ , err := parseConfig (b , opts )
780+ r := bytes .NewReader (b )
781+ decoder := yaml .NewDecoder (r )
782+
783+ baseFile , _ , err := parseConfig (decoder , opts )
758784 if err != nil {
759785 return nil , err
760786 }
0 commit comments