package workflow import ( "errors" "io" "io/fs" "os" "path/filepath" lop "github.com/samber/lo/parallel" "gopkg.in/yaml.v3" ) type WorkflowFiles []io.Reader func (wo WorkflowFiles) Close() error { var errs []error for _, reader := range wo { if reader, ok := reader.(io.ReadCloser); ok { errs = append(errs, reader.Close()) } } return errors.Join(errs...) } func (wo WorkflowFiles) Workflows() ([]Workflow, error) { var ( errs = []error{} workflows = make([]Workflow, 0, len(wo)) ) lop.ForEach(wo, func(reader io.Reader, _ int) { var w Workflow if err := yaml.NewDecoder(reader).Decode(&w); err != nil { errs = append(errs, err) } else { workflows = append(workflows, w) } if rc, ok := reader.(io.ReadCloser); ok { if err := rc.Close(); err != nil { errs = append(errs, err) } } }) return workflows, errors.Join(errs...) } type DirFilter = func(path string, d fs.FileInfo) bool // OpenDir opens a directory and returns a WorkflowInput. // // Set filter to filter out unwanted files. func OpenDir(dir string, filters ...DirFilter) (WorkflowFiles, error) { var files []io.Reader err := filepath.Walk(dir, func(path string, info fs.FileInfo, err error) error { if err != nil { return err } if info.IsDir() { return nil } ok := true for _, filter := range filters { ok = filter(path, info) if !ok { break } } if ok { file, err := os.Open(path) if err != nil { return err } files = append(files, file) } return nil }) if err != nil { return nil, err } return WorkflowFiles(files), nil } // WorkflowFromReaders creates a WorkflowInput from a list of io.Reader. // // If the Reader implements io.ReadCloser, it will be used as is. // If not, it will be wrapped with io.NopCloser. // // WorkflowFromReaders assumes ownership of the Readers and will close (if implements io.ReadCloser) // all of them after the WorkflowInput is consumed (whether successfully or not). func WorkflowFromReaders(readers ...io.Reader) WorkflowFiles { return WorkflowFiles(readers) }