diff --git a/pkg/runner/expression.go b/pkg/runner/expression.go index 272575c..7f9e02d 100644 --- a/pkg/runner/expression.go +++ b/pkg/runner/expression.go @@ -466,9 +466,19 @@ func (rc *RunContext) vmJob() func(*otto.Otto) { } func (rc *RunContext) vmSteps() func(*otto.Otto) { - steps := rc.getStepsContext() + ctxSteps := rc.getStepsContext() + + steps := make(map[string]interface{}) + for id, ctxStep := range ctxSteps { + steps[id] = map[string]interface{}{ + "conclusion": ctxStep.Conclusion.String(), + "outcome": ctxStep.Outcome.String(), + "outputs": ctxStep.Outputs, + } + } return func(vm *otto.Otto) { + log.Debugf("context steps => %v", steps) _ = vm.Set("steps", steps) } } diff --git a/pkg/runner/expression_test.go b/pkg/runner/expression_test.go index 904a20e..be9bb46 100644 --- a/pkg/runner/expression_test.go +++ b/pkg/runner/expression_test.go @@ -49,22 +49,25 @@ func TestEvaluate(t *testing.T) { }, StepResults: map[string]*stepResult{ "idwithnothing": { + Conclusion: stepStatusSuccess, + Outcome: stepStatusFailure, Outputs: map[string]string{ "foowithnothing": "barwithnothing", }, - Success: true, }, "id-with-hyphens": { + Conclusion: stepStatusSuccess, + Outcome: stepStatusFailure, Outputs: map[string]string{ "foo-with-hyphens": "bar-with-hyphens", }, - Success: true, }, "id_with_underscores": { + Conclusion: stepStatusSuccess, + Outcome: stepStatusFailure, Outputs: map[string]string{ "foo_with_underscores": "bar_with_underscores", }, - Success: true, }, }, } @@ -107,8 +110,14 @@ func TestEvaluate(t *testing.T) { {"github.run_id", "1", ""}, {"github.run_number", "1", ""}, {"job.status", "success", ""}, + {"steps.idwithnothing.conclusion", "success", ""}, + {"steps.idwithnothing.outcome", "failure", ""}, {"steps.idwithnothing.outputs.foowithnothing", "barwithnothing", ""}, + {"steps.id-with-hyphens.conclusion", "success", ""}, + {"steps.id-with-hyphens.outcome", "failure", ""}, {"steps.id-with-hyphens.outputs.foo-with-hyphens", "bar-with-hyphens", ""}, + {"steps.id_with_underscores.conclusion", "success", ""}, + {"steps.id_with_underscores.outcome", "failure", ""}, {"steps.id_with_underscores.outputs.foo_with_underscores", "bar_with_underscores", ""}, {"runner.os", "Linux", ""}, {"matrix.os", "Linux", ""}, diff --git a/pkg/runner/run_context.go b/pkg/runner/run_context.go index 40a65f2..50c59cd 100755 --- a/pkg/runner/run_context.go +++ b/pkg/runner/run_context.go @@ -52,9 +52,44 @@ func (rc *RunContext) String() string { return fmt.Sprintf("%s/%s", rc.Run.Workflow.Name, rc.Name) } +type stepStatus int + +const ( + stepStatusSuccess stepStatus = iota + stepStatusFailure +) + +var stepStatusStrings = [...]string{ + "success", + "failure", +} + +func (s stepStatus) MarshalText() ([]byte, error) { + return []byte(s.String()), nil +} + +func (s *stepStatus) UnmarshalText(b []byte) error { + str := string(b) + for i, name := range stepStatusStrings { + if name == str { + *s = stepStatus(i) + return nil + } + } + return fmt.Errorf("invalid step status %q", str) +} + +func (s stepStatus) String() string { + if int(s) >= len(stepStatusStrings) { + return "" + } + return stepStatusStrings[s] +} + type stepResult struct { - Success bool `json:"success"` - Outputs map[string]string `json:"outputs"` + Outputs map[string]string `json:"outputs"` + Conclusion stepStatus `json:"conclusion"` + Outcome stepStatus `json:"outcome"` } // GetEnv returns the env for the context @@ -266,8 +301,9 @@ func (rc *RunContext) newStepExecutor(step *model.Step) common.Executor { return func(ctx context.Context) error { rc.CurrentStep = sc.Step.ID rc.StepResults[rc.CurrentStep] = &stepResult{ - Success: true, - Outputs: make(map[string]string), + Outcome: stepStatusSuccess, + Conclusion: stepStatusSuccess, + Outputs: make(map[string]string), } runStep, err := rc.EvalBool(sc.Step.If.Value) @@ -278,7 +314,8 @@ func (rc *RunContext) newStepExecutor(step *model.Step) common.Executor { return err } rc.ExprEval = exprEval - rc.StepResults[rc.CurrentStep].Success = false + rc.StepResults[rc.CurrentStep].Conclusion = stepStatusFailure + rc.StepResults[rc.CurrentStep].Outcome = stepStatusFailure return err } @@ -300,12 +337,13 @@ func (rc *RunContext) newStepExecutor(step *model.Step) common.Executor { } else { common.Logger(ctx).Errorf(" \u274C Failure - %s", sc.Step) + rc.StepResults[rc.CurrentStep].Outcome = stepStatusFailure if sc.Step.ContinueOnError { common.Logger(ctx).Infof("Failed but continue next step") err = nil - rc.StepResults[rc.CurrentStep].Success = true + rc.StepResults[rc.CurrentStep].Conclusion = stepStatusSuccess } else { - rc.StepResults[rc.CurrentStep].Success = false + rc.StepResults[rc.CurrentStep].Conclusion = stepStatusFailure } } return err @@ -500,7 +538,7 @@ type jobContext struct { func (rc *RunContext) getJobContext() *jobContext { jobStatus := "success" for _, stepStatus := range rc.StepResults { - if !stepStatus.Success { + if stepStatus.Conclusion == stepStatusFailure { jobStatus = "failure" break } diff --git a/pkg/runner/run_context_test.go b/pkg/runner/run_context_test.go index 6d1cb38..0419f4a 100644 --- a/pkg/runner/run_context_test.go +++ b/pkg/runner/run_context_test.go @@ -54,10 +54,11 @@ func TestRunContext_EvalBool(t *testing.T) { }, StepResults: map[string]*stepResult{ "id1": { + Conclusion: stepStatusSuccess, + Outcome: stepStatusFailure, Outputs: map[string]string{ "foo": "bar", }, - Success: true, }, }, } @@ -73,6 +74,10 @@ func TestRunContext_EvalBool(t *testing.T) { {in: "success()", out: true}, {in: "cancelled()", out: false}, {in: "always()", out: true}, + {in: "steps.id1.conclusion == 'success'", out: true}, + {in: "steps.id1.conclusion != 'success'", out: false}, + {in: "steps.id1.outcome == 'failure'", out: true}, + {in: "steps.id1.outcome != 'failure'", out: false}, {in: "true", out: true}, {in: "false", out: false}, {in: "!true", wantErr: true}, diff --git a/pkg/runner/runner_test.go b/pkg/runner/runner_test.go index 6190164..bffa0d6 100644 --- a/pkg/runner/runner_test.go +++ b/pkg/runner/runner_test.go @@ -121,6 +121,8 @@ func TestRunEvent(t *testing.T) { {"testdata", "issue-598", "push", "", platforms, ""}, {"testdata", "env-and-path", "push", "", platforms, ""}, {"testdata", "outputs", "push", "", platforms, ""}, + {"testdata", "steps-context/conclusion", "push", "", platforms, ""}, + {"testdata", "steps-context/outcome", "push", "", platforms, ""}, {"../model/testdata", "strategy", "push", "", platforms, ""}, // TODO: move all testdata into pkg so we can validate it with planner and runner // {"testdata", "issue-228", "push", "", platforms, ""}, // TODO [igni]: Remove this once everything passes diff --git a/pkg/runner/step_context.go b/pkg/runner/step_context.go index 13c01d1..367d7fe 100644 --- a/pkg/runner/step_context.go +++ b/pkg/runner/step_context.go @@ -640,8 +640,8 @@ func (sc *StepContext) execAsComposite(ctx context.Context, step *model.Step, _ // Setup the outputs for the composite steps if _, ok := rcClone.StepResults[stepClone.ID]; !ok { rcClone.StepResults[stepClone.ID] = &stepResult{ - Success: true, - Outputs: make(map[string]string), + Conclusion: stepStatusSuccess, + Outputs: make(map[string]string), } } diff --git a/pkg/runner/testdata/steps-context/conclusion/push.yml b/pkg/runner/testdata/steps-context/conclusion/push.yml new file mode 100644 index 0000000..aff6bb4 --- /dev/null +++ b/pkg/runner/testdata/steps-context/conclusion/push.yml @@ -0,0 +1,14 @@ +name: conclusion +on: push + +jobs: + check: + runs-on: ubuntu-latest + steps: + - id: first + run: exit 0 + - id: second + continue-on-error: true + run: exit 1 + - run: echo '${{ steps.first.conclusion }}' | grep 'success' + - run: echo '${{ steps.second.conclusion }}' | grep 'success' diff --git a/pkg/runner/testdata/steps-context/outcome/push.yml b/pkg/runner/testdata/steps-context/outcome/push.yml new file mode 100644 index 0000000..20f8e81 --- /dev/null +++ b/pkg/runner/testdata/steps-context/outcome/push.yml @@ -0,0 +1,14 @@ +name: outcome +on: push + +jobs: + check: + runs-on: ubuntu-latest + steps: + - id: first + run: exit 0 + - id: second + continue-on-error: true + run: exit 1 + - run: echo '${{ steps.first.outcome }}' | grep 'success' + - run: echo '${{ steps.second.outcome }}' | grep 'failure'