diff --git a/internal/actions/definition.go b/internal/actions/definition.go new file mode 100644 index 0000000..207b1f6 --- /dev/null +++ b/internal/actions/definition.go @@ -0,0 +1,37 @@ +package actions + +import ( + "gopkg.in/yaml.v3" + "io/ioutil" +) + +type ActionInput struct { + Description string `yaml:"description"` + Default string `yaml:"default"` +} + +type ActionDefinition struct { + Name string `yaml:"name"` + Description string `yaml:"description"` + Inputs map[string]ActionInput `yaml:"inputs"` + Runs struct { + Using string `yaml:"using"` + Main string `yaml:"main"` + } `yaml:"runs"` +} + +func GetDefinitionFromFile(defPath string) (*ActionDefinition, error) { + defRaw, err := ioutil.ReadFile(defPath) + + if err != nil { + return nil, err + } + + var def ActionDefinition + + if yamlError := yaml.Unmarshal(defRaw, &def); yamlError != nil { + return nil, yamlError + } + + return &def, nil +} diff --git a/internal/actions/manager.go b/internal/actions/manager.go index 24cf2d6..76be023 100644 --- a/internal/actions/manager.go +++ b/internal/actions/manager.go @@ -1,16 +1,23 @@ package actions import ( + driver "courgette/internal/driver" logger "courgette/internal/logging" + "errors" + "fmt" + "path/filepath" ) type ActionsManager struct { git CliClient + // Action cache location. + cacheRoot string } -func NewActionsManager() ActionsManager { +func NewActionsManager(cacheRoot string) ActionsManager { return ActionsManager{ - git: GitClient{}, + git: GitClient{}, + cacheRoot: cacheRoot, } } @@ -18,8 +25,32 @@ func NewActionsManager() ActionsManager { // // is expected to be the full url of the repository where // the action lives. -func (a ActionsManager) PrefetchAction(actionName string, destination string) error { +func (a ActionsManager) PrefetchAction(actionName string) error { + destination := filepath.Join(a.cacheRoot, GetActionKey(actionName)) logger.Info("Prefetching action: %s to %s", actionName, destination) return a.git.Clone(actionName, destination) } + +func (a ActionsManager) UseAction(actionName string, containerId string, options driver.CommandOptions) error { + actionKey := GetActionKey(actionName) + hostActionRoot := filepath.Join(a.cacheRoot, actionKey) + actionDefPath := filepath.Join(hostActionRoot, "action.yml") + actionDef, _ := GetDefinitionFromFile(actionDefPath) + + containerActionRoot := filepath.Join("/cache", actionKey) + actionMain := filepath.Join(containerActionRoot, actionDef.Runs.Main) + command := fmt.Sprintf("node %s", actionMain) + + result := driver.PodmanDriver{}.Exec(containerId, command, options) + + if result.Error != nil { + return result.Error + } + + if result.ExitCode != 0 { + return errors.New("Action used returned non-zero exit code.") + } + + return nil +} diff --git a/internal/runner/runner.go b/internal/runner/runner.go index f4f7f23..f7fe660 100644 --- a/internal/runner/runner.go +++ b/internal/runner/runner.go @@ -49,7 +49,7 @@ func (r *Runner) RunWorkflow(workflow workflows.Workflow) TaskTracker { switch n.(type) { case workflows.Step: if useAction := n.(workflows.Step).Use; useAction != "" { - actions.NewActionsManager().PrefetchAction(useAction, r.Cache.Path(actions.GetActionKey(useAction))) + actions.NewActionsManager(r.Cache.Root).PrefetchAction(useAction) } } }) @@ -164,6 +164,13 @@ func (r *Runner) RunJobInContainer(imageUri string, containerId string, jobConte Shell: stepShell, } stepError = r.RunCommandInContainer(containerId, step.Run, commandOptions) + } else if step.Use != "" { + commandOptions := driver.CommandOptions{ + Cwd: stepCwd, + Env: stepEnv, + Shell: stepShell, + } + stepError = actions.NewActionsManager(r.Cache.Root).UseAction(step.Use, containerId, commandOptions) } if stepError != nil && !step.ContinueOnError {