From 33ccfa6f3b85bfe02716e3369f19246de450c9e7 Mon Sep 17 00:00:00 2001 From: "Ryan (hackercat)" Date: Sun, 6 Jun 2021 14:53:18 +0000 Subject: [PATCH] Switch to `interface{}` instead of `map[string]...` (#700) * fix: change `env` to be an interface allows to use GitHub functions like `fromJson()` * fix: change matrix to an interface instead of map This allows to use GitHub functions like `fromJson()` to create dynamic matrixes Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> --- pkg/model/workflow.go | 88 ++++++++++++++++++++++++++++++-------- pkg/runner/run_context.go | 2 +- pkg/runner/step_context.go | 6 ++- 3 files changed, 76 insertions(+), 20 deletions(-) diff --git a/pkg/model/workflow.go b/pkg/model/workflow.go index c5b1191..9920c55 100644 --- a/pkg/model/workflow.go +++ b/pkg/model/workflow.go @@ -58,7 +58,7 @@ type Job struct { Name string `yaml:"name"` RawNeeds yaml.Node `yaml:"needs"` RawRunsOn yaml.Node `yaml:"runs-on"` - Env map[string]string `yaml:"env"` + Env interface{} `yaml:"env"` If yaml.Node `yaml:"if"` Steps []*Step `yaml:"steps"` TimeoutMinutes int64 `yaml:"timeout-minutes"` @@ -70,9 +70,9 @@ type Job struct { // Strategy for the job type Strategy struct { - FailFast bool `yaml:"fail-fast"` - MaxParallel int `yaml:"max-parallel"` - Matrix map[string][]interface{} `yaml:"matrix"` + FailFast bool `yaml:"fail-fast"` + MaxParallel int `yaml:"max-parallel"` + Matrix interface{} `yaml:"matrix"` } // Default settings that will apply to all steps in the job or workflow @@ -148,23 +148,75 @@ func (j *Job) RunsOn() []string { return nil } +func environment(e interface{}) map[string]string { + env := make(map[string]string) + switch t := e.(type) { + case map[string]interface{}: + for k, v := range t { + switch t := v.(type) { + case string: + env[k] = t + case interface{}: + env[k] = "" + } + } + case map[string]string: + for k, v := range e.(map[string]string) { + env[k] = v + } + } + return env +} + +func (j *Job) Environment() map[string]string { + return environment(j.Env) +} + +func (j *Job) Matrix() map[string][]interface{} { + a := reflect.ValueOf(j.Strategy.Matrix) + if a.Type().Kind() == reflect.Map { + output := make(map[string][]interface{}) + for _, e := range a.MapKeys() { + v := a.MapIndex(e) + switch t := v.Interface().(type) { + case []interface{}: + output[e.String()] = t + case interface{}: + var in []interface{} + in = append(in, t) + output[e.String()] = in + } + } + return output + } + return nil +} + // GetMatrixes returns the matrix cross product func (j *Job) GetMatrixes() []map[string]interface{} { matrixes := make([]map[string]interface{}, 0) if j.Strategy != nil { + m := j.Matrix() includes := make([]map[string]interface{}, 0) - for _, v := range j.Strategy.Matrix["include"] { - includes = append(includes, v.(map[string]interface{})) + for _, v := range m["include"] { + switch t := v.(type) { + case []interface{}: + for _, i := range t { + includes = append(includes, i.(map[string]interface{})) + } + case interface{}: + includes = append(includes, v.(map[string]interface{})) + } } - delete(j.Strategy.Matrix, "include") + delete(m, "include") excludes := make([]map[string]interface{}, 0) - for _, v := range j.Strategy.Matrix["exclude"] { + for _, v := range m["exclude"] { excludes = append(excludes, v.(map[string]interface{})) } - delete(j.Strategy.Matrix, "exclude") + delete(m, "exclude") - matrixProduct := common.CartesianProduct(j.Strategy.Matrix) + matrixProduct := common.CartesianProduct(m) MATRIX: for _, matrix := range matrixProduct { @@ -217,7 +269,7 @@ type Step struct { Run string `yaml:"run"` WorkingDirectory string `yaml:"working-directory"` Shell string `yaml:"shell"` - Env map[string]string `yaml:"env"` + Env interface{} `yaml:"env"` With map[string]string `yaml:"with"` ContinueOnError bool `yaml:"continue-on-error"` TimeoutMinutes int64 `yaml:"timeout-minutes"` @@ -235,18 +287,20 @@ func (s *Step) String() string { return s.ID } +func (s *Step) Environment() map[string]string { + return environment(s.Env) +} + // GetEnv gets the env for a step func (s *Step) GetEnv() map[string]string { - rtnEnv := make(map[string]string) - for k, v := range s.Env { - rtnEnv[k] = v - } + env := s.Environment() + for k, v := range s.With { envKey := regexp.MustCompile("[^A-Z0-9-]").ReplaceAllString(strings.ToUpper(k), "_") envKey = fmt.Sprintf("INPUT_%s", strings.ToUpper(envKey)) - rtnEnv[envKey] = v + env[envKey] = v } - return rtnEnv + return env } // ShellCommand returns the command for the shell diff --git a/pkg/runner/run_context.go b/pkg/runner/run_context.go index 5c52cf3..151386b 100755 --- a/pkg/runner/run_context.go +++ b/pkg/runner/run_context.go @@ -55,7 +55,7 @@ type stepResult struct { // GetEnv returns the env for the context func (rc *RunContext) GetEnv() map[string]string { if rc.Env == nil { - rc.Env = mergeMaps(rc.Config.Env, rc.Run.Workflow.Env, rc.Run.Job().Env) + rc.Env = mergeMaps(rc.Config.Env, rc.Run.Workflow.Env, rc.Run.Job().Environment()) } rc.Env["ACT"] = "true" return rc.Env diff --git a/pkg/runner/step_context.go b/pkg/runner/step_context.go index 690614a..9d6eb38 100644 --- a/pkg/runner/step_context.go +++ b/pkg/runner/step_context.go @@ -574,13 +574,15 @@ func (sc *StepContext) execAsComposite(ctx context.Context, step *model.Step, _ stepClone.Env = make(map[string]string) } actionPath := filepath.Join(containerActionDir, actionName) - stepClone.Env["GITHUB_ACTION_PATH"] = actionPath + + env := stepClone.Environment() + env["GITHUB_ACTION_PATH"] = actionPath stepClone.Run = strings.ReplaceAll(stepClone.Run, "${{ github.action_path }}", actionPath) stepContext := StepContext{ RunContext: rcClone, Step: &stepClone, - Env: mergeMaps(sc.Env, stepClone.Env), + Env: mergeMaps(sc.Env, env), } // Interpolate the outer inputs into the composite step with items