2024-06-27 16:47:11 +07:00
|
|
|
package workflow
|
|
|
|
|
|
|
|
import (
|
|
|
|
"errors"
|
|
|
|
"io"
|
|
|
|
"io/fs"
|
|
|
|
"os"
|
|
|
|
"path/filepath"
|
|
|
|
|
2024-07-03 21:57:45 +07:00
|
|
|
lop "github.com/samber/lo/parallel"
|
2024-06-27 16:47:11 +07:00
|
|
|
"gopkg.in/yaml.v3"
|
|
|
|
)
|
|
|
|
|
2024-07-03 21:57:45 +07:00
|
|
|
type WorkflowFiles []io.Reader
|
2024-06-27 16:47:11 +07:00
|
|
|
|
|
|
|
func (wo WorkflowFiles) Close() error {
|
|
|
|
var errs []error
|
2024-07-03 21:57:45 +07:00
|
|
|
for _, reader := range wo {
|
|
|
|
if reader, ok := reader.(io.ReadCloser); ok {
|
|
|
|
errs = append(errs, reader.Close())
|
2024-06-27 16:47:11 +07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return errors.Join(errs...)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (wo WorkflowFiles) Workflows() ([]Workflow, error) {
|
|
|
|
var (
|
2024-07-03 21:57:45 +07:00
|
|
|
errs = []error{}
|
|
|
|
workflows = make([]Workflow, 0, len(wo))
|
2024-06-27 16:47:11 +07:00
|
|
|
)
|
|
|
|
|
2024-07-03 21:57:45 +07:00
|
|
|
lop.ForEach(wo, func(reader io.Reader, _ int) {
|
2024-06-27 16:47:11 +07:00
|
|
|
var w Workflow
|
2024-07-03 21:57:45 +07:00
|
|
|
if err := yaml.NewDecoder(reader).Decode(&w); err != nil {
|
|
|
|
errs = append(errs, err)
|
2024-06-27 16:47:11 +07:00
|
|
|
} else {
|
2024-07-03 21:57:45 +07:00
|
|
|
workflows = append(workflows, w)
|
2024-06-27 16:47:11 +07:00
|
|
|
}
|
2024-07-03 21:57:45 +07:00
|
|
|
if rc, ok := reader.(io.ReadCloser); ok {
|
|
|
|
if err := rc.Close(); err != nil {
|
|
|
|
errs = append(errs, err)
|
|
|
|
}
|
2024-06-27 16:47:11 +07:00
|
|
|
}
|
|
|
|
})
|
|
|
|
|
|
|
|
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) {
|
2024-07-03 21:57:45 +07:00
|
|
|
var files []io.Reader
|
2024-06-27 16:47:11 +07:00
|
|
|
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 {
|
2024-07-03 21:57:45 +07:00
|
|
|
return WorkflowFiles(readers)
|
2024-06-27 16:47:11 +07:00
|
|
|
}
|