package workflow import ( "time" "git.bazzel.dev/bmallen/helios/pkg/state" "github.com/sirupsen/logrus" ) type ( Workflow struct { name string jobs jobs c Context start time.Time end time.Time log *logrus.Logger } job struct { name string steps steps needs []string workflow *Workflow start time.Time end time.Time } jobs []*job jobOption interface { Job(j *job) } step struct { name string f StepFunc start time.Time end time.Time workflow *Workflow job *job } steps []*step needs struct { name string } Context interface { Get(key string) any Set(key string, value any) Delete(key string) Keys() []string Range(f func(key string, value any) bool) ID() string } StepFunc func(ctx Context) error ) func NewWorkflow(name string) *Workflow { return &Workflow{ name: name, jobs: jobs{}, c: state.New(), log: logrus.New(), } } func (w *Workflow) Run() (err error) { w.start = time.Now() defer func() { w.end = time.Now() }() for _, j := range w.jobs { j.start = time.Now() w.log.Printf("# Job: %s\n", j.name) for _, step := range j.steps { w.log.Printf("- Step: %s / %s\n", j.name, step.name) err := step.Run() w.log.Printf(" Duration: %s\n", step.Duration()) if err != nil { break } } j.end = time.Now() if err != nil { break } } return err } func (w *Workflow) Job(name string, opts ...jobOption) *Workflow { job := &job{ name: name, steps: steps{}, needs: []string{}, workflow: w, } for _, opt := range opts { opt.Job(job) } w.jobs = append(w.jobs, job) return w } func Step(name string, f StepFunc) jobOption { return &step{ name: name, f: f, } } func (s *step) Job(j *job) { s.job = j s.workflow = j.workflow j.steps = append(j.steps, s) } func (s *step) Run() error { s.start = time.Now() err := s.f(s.workflow.c) s.end = time.Now() return err } func (s *step) Duration() time.Duration { return s.end.Sub(s.start) } func Needs(name string) jobOption { return &needs{ name: name, } } func (o *needs) Job(j *job) { j.needs = append(j.needs, o.name) }