courgette/internal/workflow/models.go

123 lines
2.7 KiB
Go

package workflow
import (
"errors"
)
type Job struct {
RunsOn string `yaml:"runs-on"`
Steps []Step `yaml:"steps"`
Needs []string `yaml:"needs"`
// Job name; this isn't guaranteed to be unique.
Name string `yaml:"name"`
Defaults struct {
Run struct {
WorkingDirectory string `yaml:"working-directory"`
} `yaml:"run"`
} `yaml:"defaults"`
}
func (j Job) Validate() []error {
validationErrors := []error{}
if j.RunsOn == "" {
validationErrors = append(validationErrors, errors.New("Missing \"runs-on\" field on job."))
}
if len(j.Steps) == 0 {
validationErrors = append(validationErrors, errors.New("Missing \"steps\" field on job."))
}
for _, step := range j.Steps {
validationErrors = append(validationErrors, step.Validate()...)
}
return validationErrors
}
type Step struct {
Run string `yaml:"run"`
WorkingDirectory string `yaml:"working-directory"`
}
func (s Step) Validate() []error {
validationErrors := []error{}
if s.Run == "" {
validationErrors = append(validationErrors, errors.New("Missing \"run\" field on step."))
}
return validationErrors
}
type Workflow struct {
SourcePath string
Jobs map[string]Job `yaml:"jobs"`
}
// Returns the given workflow's job+step working directory inside the container
// that runs the job.
//
// Values considered, in order:
// - Step working-directory;
// - Job default run working-directory
func (w Workflow) GetWorkingDirectory(jobName string, stepIndex int) string {
jobDefinition := w.Jobs[jobName]
stepDefinition := jobDefinition.Steps[stepIndex]
if stepDefinition.WorkingDirectory != "" {
return stepDefinition.WorkingDirectory
}
if jobDefinition.Defaults.Run.WorkingDirectory != "" {
return jobDefinition.Defaults.Run.WorkingDirectory
}
return "."
}
func (w Workflow) Validate() []error {
validationErrors := []error{}
if len(w.Jobs) == 0 {
validationErrors = append(validationErrors, errors.New("Missing \"jobs\" field on workflow."))
}
for _, job := range w.Jobs {
validationErrors = append(validationErrors, job.Validate()...)
}
return validationErrors
}
// Creates a deterministic, ordered collection of jobs that respects
// the jobs's dependencies.
func (w Workflow) GetJobsAsGroups() [][]Job {
dependenciesMap := map[string][]string{}
for jobLabel, job := range w.Jobs {
if len(job.Needs) == 0 {
dependenciesMap[""] = append(dependenciesMap[""], jobLabel)
}
for _, need := range job.Needs {
dependenciesMap[need] = append(dependenciesMap[need], jobLabel)
}
}
levels := SplitFlatTreeIntoGroups(dependenciesMap)
groups := [][]Job{}
for _, jobLabels := range levels {
jobs := []Job{}
for _, jobLabel := range jobLabels {
jobs = append(jobs, w.Jobs[jobLabel])
}
groups = append(groups, jobs)
}
return groups
}