diff --git a/pkg/model/workflow.go b/pkg/model/workflow.go index 35e4587..68f7b68 100644 --- a/pkg/model/workflow.go +++ b/pkg/model/workflow.go @@ -248,15 +248,13 @@ func (j *Job) Container() *ContainerSpec { switch j.RawContainer.Kind { case yaml.ScalarNode: val = new(ContainerSpec) - err := j.RawContainer.Decode(&val.Image) - if err != nil { - log.Fatal(err) + if !decodeNode(j.RawContainer, &val.Image) { + return nil } case yaml.MappingNode: val = new(ContainerSpec) - err := j.RawContainer.Decode(val) - if err != nil { - log.Fatal(err) + if !decodeNode(j.RawContainer, val) { + return nil } } return val @@ -267,16 +265,14 @@ func (j *Job) Needs() []string { switch j.RawNeeds.Kind { case yaml.ScalarNode: var val string - err := j.RawNeeds.Decode(&val) - if err != nil { - log.Fatal(err) + if !decodeNode(j.RawNeeds, &val) { + return nil } return []string{val} case yaml.SequenceNode: var val []string - err := j.RawNeeds.Decode(&val) - if err != nil { - log.Fatal(err) + if !decodeNode(j.RawNeeds, &val) { + return nil } return val } @@ -288,16 +284,14 @@ func (j *Job) RunsOn() []string { switch j.RawRunsOn.Kind { case yaml.ScalarNode: var val string - err := j.RawRunsOn.Decode(&val) - if err != nil { - log.Fatal(err) + if !decodeNode(j.RawRunsOn, &val) { + return nil } return []string{val} case yaml.SequenceNode: var val []string - err := j.RawRunsOn.Decode(&val) - if err != nil { - log.Fatal(err) + if !decodeNode(j.RawRunsOn, &val) { + return nil } return val } @@ -307,8 +301,8 @@ func (j *Job) RunsOn() []string { func environment(yml yaml.Node) map[string]string { env := make(map[string]string) if yml.Kind == yaml.MappingNode { - if err := yml.Decode(&env); err != nil { - log.Fatal(err) + if !decodeNode(yml, &env) { + return nil } } return env @@ -323,8 +317,8 @@ func (j *Job) Environment() map[string]string { func (j *Job) Matrix() map[string][]interface{} { if j.Strategy.RawMatrix.Kind == yaml.MappingNode { var val map[string][]interface{} - if err := j.Strategy.RawMatrix.Decode(&val); err != nil { - log.Fatal(err) + if !decodeNode(j.Strategy.RawMatrix, &val) { + return nil } return val } @@ -335,7 +329,7 @@ func (j *Job) Matrix() map[string][]interface{} { // It skips includes and hard fails excludes for non-existing keys // //nolint:gocyclo -func (j *Job) GetMatrixes() []map[string]interface{} { +func (j *Job) GetMatrixes() ([]map[string]interface{}, error) { matrixes := make([]map[string]interface{}, 0) if j.Strategy != nil { j.Strategy.FailFast = j.Strategy.GetFailFast() @@ -386,7 +380,7 @@ func (j *Job) GetMatrixes() []map[string]interface{} { excludes = append(excludes, e) } else { // We fail completely here because that's what GitHub does for non-existing matrix keys, fail on exclude, silent skip on include - log.Fatalf("The workflow is not valid. Matrix exclude key '%s' does not match any key within the matrix", k) + return nil, fmt.Errorf("the workflow is not valid. Matrix exclude key %q does not match any key within the matrix", k) } } } @@ -431,7 +425,7 @@ func (j *Job) GetMatrixes() []map[string]interface{} { } else { matrixes = append(matrixes, make(map[string]interface{})) } - return matrixes + return matrixes, nil } func commonKeysMatch(a map[string]interface{}, b map[string]interface{}) bool { @@ -671,3 +665,17 @@ func (w *Workflow) GetJobIDs() []string { } return ids } + +var OnDecodeNodeError = func(node yaml.Node, out interface{}, err error) { + log.Fatalf("Failed to decode node %v into %T: %v", node, out, err) +} + +func decodeNode(node yaml.Node, out interface{}) bool { + if err := node.Decode(out); err != nil { + if OnDecodeNodeError != nil { + OnDecodeNodeError(node, out, err) + } + return false + } + return true +} diff --git a/pkg/model/workflow_test.go b/pkg/model/workflow_test.go index a789233..8f8b7a9 100644 --- a/pkg/model/workflow_test.go +++ b/pkg/model/workflow_test.go @@ -250,25 +250,33 @@ func TestReadWorkflow_Strategy(t *testing.T) { wf := p.Stages[0].Runs[0].Workflow job := wf.Jobs["strategy-only-max-parallel"] - assert.Equal(t, job.GetMatrixes(), []map[string]interface{}{{}}) + matrixes, err := job.GetMatrixes() + assert.NoError(t, err) + assert.Equal(t, matrixes, []map[string]interface{}{{}}) assert.Equal(t, job.Matrix(), map[string][]interface{}(nil)) assert.Equal(t, job.Strategy.MaxParallel, 2) assert.Equal(t, job.Strategy.FailFast, true) job = wf.Jobs["strategy-only-fail-fast"] - assert.Equal(t, job.GetMatrixes(), []map[string]interface{}{{}}) + matrixes, err = job.GetMatrixes() + assert.NoError(t, err) + assert.Equal(t, matrixes, []map[string]interface{}{{}}) assert.Equal(t, job.Matrix(), map[string][]interface{}(nil)) assert.Equal(t, job.Strategy.MaxParallel, 4) assert.Equal(t, job.Strategy.FailFast, false) job = wf.Jobs["strategy-no-matrix"] - assert.Equal(t, job.GetMatrixes(), []map[string]interface{}{{}}) + matrixes, err = job.GetMatrixes() + assert.NoError(t, err) + assert.Equal(t, matrixes, []map[string]interface{}{{}}) assert.Equal(t, job.Matrix(), map[string][]interface{}(nil)) assert.Equal(t, job.Strategy.MaxParallel, 2) assert.Equal(t, job.Strategy.FailFast, false) job = wf.Jobs["strategy-all"] - assert.Equal(t, job.GetMatrixes(), + matrixes, err = job.GetMatrixes() + assert.NoError(t, err) + assert.Equal(t, matrixes, []map[string]interface{}{ {"datacenter": "site-c", "node-version": "14.x", "site": "staging"}, {"datacenter": "site-c", "node-version": "16.x", "site": "staging"}, diff --git a/pkg/runner/run_context.go b/pkg/runner/run_context.go index 1273811..bbd8ec4 100644 --- a/pkg/runner/run_context.go +++ b/pkg/runner/run_context.go @@ -18,7 +18,6 @@ import ( "strings" "github.com/opencontainers/selinux/go-selinux" - log "github.com/sirupsen/logrus" "github.com/nektos/act/pkg/common" "github.com/nektos/act/pkg/container" @@ -361,7 +360,8 @@ func (rc *RunContext) ActionCacheDir() string { if home, err := os.UserHomeDir(); err == nil { xdgCache = filepath.Join(home, ".cache") } else if xdgCache, err = filepath.Abs("."); err != nil { - log.Fatal(err) + // It's almost impossible to get here, so the temp dir is a good fallback + xdgCache = os.TempDir() } } return filepath.Join(xdgCache, "act") diff --git a/pkg/runner/runner.go b/pkg/runner/runner.go index 715d158..a47cf8b 100644 --- a/pkg/runner/runner.go +++ b/pkg/runner/runner.go @@ -118,7 +118,12 @@ func (runner *runnerImpl) NewPlanExecutor(plan *model.Plan) common.Executor { } } - matrixes := selectMatrixes(job.GetMatrixes(), runner.config.Matrix) + var matrixes []map[string]interface{} + if m, err := job.GetMatrixes(); err != nil { + log.Errorf("Error while get job's matrix: %v", err) + } else { + matrixes = selectMatrixes(m, runner.config.Matrix) + } log.Debugf("Final matrix after applying user inclusions '%v'", matrixes) maxParallel := 4