act/actions/runner.go

161 lines
4.1 KiB
Go

package actions
import (
"io/ioutil"
"os"
"path/filepath"
"sort"
"github.com/actions/workflow-parser/model"
"github.com/actions/workflow-parser/parser"
"github.com/nektos/act/common"
log "github.com/sirupsen/logrus"
)
type runnerImpl struct {
config *RunnerConfig
workflowConfig *model.Configuration
tempDir string
eventJSON string
}
// NewRunner Creates a new Runner
func NewRunner(runnerConfig *RunnerConfig) (Runner, error) {
runner := &runnerImpl{
config: runnerConfig,
}
init := common.NewPipelineExecutor(
runner.setupTempDir,
runner.setupWorkingDir,
runner.setupWorkflows,
runner.setupEvent,
)
return runner, init()
}
func (runner *runnerImpl) setupTempDir() error {
var err error
runner.tempDir, err = ioutil.TempDir("", "act-")
return err
}
func (runner *runnerImpl) setupWorkingDir() error {
var err error
runner.config.WorkingDir, err = filepath.Abs(runner.config.WorkingDir)
log.Debugf("Setting working dir to %s", runner.config.WorkingDir)
return err
}
func (runner *runnerImpl) setupWorkflows() error {
runner.config.WorkflowPath = runner.resolvePath(runner.config.WorkflowPath)
log.Debugf("Loading workflow config from %s", runner.config.WorkflowPath)
workflowReader, err := os.Open(runner.config.WorkflowPath)
if err != nil {
return err
}
defer workflowReader.Close()
runner.workflowConfig, err = parser.Parse(workflowReader)
return err
}
func (runner *runnerImpl) setupEvent() error {
runner.eventJSON = "{}"
if runner.config.EventPath != "" {
runner.config.EventPath = runner.resolvePath(runner.config.EventPath)
log.Debugf("Reading event.json from %s", runner.config.EventPath)
eventJSONBytes, err := ioutil.ReadFile(runner.config.EventPath)
if err != nil {
return err
}
runner.eventJSON = string(eventJSONBytes)
}
return nil
}
func (runner *runnerImpl) resolvePath(path string) string {
if path == "" {
return path
}
if !filepath.IsAbs(path) {
path = filepath.Join(runner.config.WorkingDir, path)
}
return path
}
// ListEvents gets all the events in the workflows file
func (runner *runnerImpl) ListEvents() []string {
log.Debugf("Listing all events")
events := make([]string, 0)
for _, w := range runner.workflowConfig.Workflows {
events = append(events, w.On)
}
// sort the list based on depth of dependencies
sort.Slice(events, func(i, j int) bool {
return events[i] < events[j]
})
return events
}
// GraphEvent builds an execution path
func (runner *runnerImpl) GraphEvent(eventName string) ([][]string, error) {
log.Debugf("Listing actions for event '%s'", eventName)
resolves := runner.resolveEvent(eventName)
return newExecutionGraph(runner.workflowConfig, resolves...), nil
}
// RunAction runs a set of actions in parallel, and their dependencies
func (runner *runnerImpl) RunActions(actionNames ...string) error {
log.Debugf("Running actions %+q", actionNames)
graph := newExecutionGraph(runner.workflowConfig, actionNames...)
pipeline := make([]common.Executor, 0)
for _, actions := range graph {
stage := make([]common.Executor, 0)
for _, actionName := range actions {
stage = append(stage, runner.newActionExecutor(actionName))
}
pipeline = append(pipeline, common.NewParallelExecutor(stage...))
}
executor := common.NewPipelineExecutor(pipeline...)
return executor()
}
// RunEvent runs the actions for a single event
func (runner *runnerImpl) RunEvent() error {
log.Debugf("Running event '%s'", runner.config.EventName)
resolves := runner.resolveEvent(runner.config.EventName)
log.Debugf("Running actions %s -> %s", runner.config.EventName, resolves)
return runner.RunActions(resolves...)
}
func (runner *runnerImpl) Close() error {
return os.RemoveAll(runner.tempDir)
}
// get list of resolves for an event
func (runner *runnerImpl) resolveEvent(eventName string) []string {
workflows := runner.workflowConfig.GetWorkflows(eventName)
resolves := make([]string, 0)
for _, workflow := range workflows {
for _, resolve := range workflow.Resolves {
found := false
for _, r := range resolves {
if r == resolve {
found = true
break
}
}
if !found {
resolves = append(resolves, resolve)
}
}
}
return resolves
}