Co-authored-by: Markus Wolf <KnisterPeter@users.noreply.github.com>
This commit is contained in:
parent
b514649c3d
commit
d1daf2f28d
3 changed files with 105 additions and 12 deletions
|
@ -350,17 +350,17 @@ type ContainerSpec struct {
|
||||||
|
|
||||||
// Step is the structure of one step in a job
|
// Step is the structure of one step in a job
|
||||||
type Step struct {
|
type Step struct {
|
||||||
ID string `yaml:"id"`
|
ID string `yaml:"id"`
|
||||||
If yaml.Node `yaml:"if"`
|
If yaml.Node `yaml:"if"`
|
||||||
Name string `yaml:"name"`
|
Name string `yaml:"name"`
|
||||||
Uses string `yaml:"uses"`
|
Uses string `yaml:"uses"`
|
||||||
Run string `yaml:"run"`
|
Run string `yaml:"run"`
|
||||||
WorkingDirectory string `yaml:"working-directory"`
|
WorkingDirectory string `yaml:"working-directory"`
|
||||||
Shell string `yaml:"shell"`
|
Shell string `yaml:"shell"`
|
||||||
Env yaml.Node `yaml:"env"`
|
Env yaml.Node `yaml:"env"`
|
||||||
With map[string]string `yaml:"with"`
|
With map[string]string `yaml:"with"`
|
||||||
ContinueOnError bool `yaml:"continue-on-error"`
|
RawContinueOnError string `yaml:"continue-on-error"`
|
||||||
TimeoutMinutes string `yaml:"timeout-minutes"`
|
TimeoutMinutes string `yaml:"timeout-minutes"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// String gets the name of step
|
// String gets the name of step
|
||||||
|
|
|
@ -99,7 +99,14 @@ func runStepExecutor(step step, stage stepStage, executor common.Executor) commo
|
||||||
logger.WithField("stepResult", rc.StepResults[rc.CurrentStep].Outcome).Infof(" \u2705 Success - %s %s", stage, stepString)
|
logger.WithField("stepResult", rc.StepResults[rc.CurrentStep].Outcome).Infof(" \u2705 Success - %s %s", stage, stepString)
|
||||||
} else {
|
} else {
|
||||||
rc.StepResults[rc.CurrentStep].Outcome = model.StepStatusFailure
|
rc.StepResults[rc.CurrentStep].Outcome = model.StepStatusFailure
|
||||||
if stepModel.ContinueOnError {
|
|
||||||
|
continueOnError, parseErr := isContinueOnError(ctx, stepModel.RawContinueOnError, step, stage)
|
||||||
|
if parseErr != nil {
|
||||||
|
rc.StepResults[rc.CurrentStep].Conclusion = model.StepStatusFailure
|
||||||
|
return parseErr
|
||||||
|
}
|
||||||
|
|
||||||
|
if continueOnError {
|
||||||
logger.Infof("Failed but continue next step")
|
logger.Infof("Failed but continue next step")
|
||||||
err = nil
|
err = nil
|
||||||
rc.StepResults[rc.CurrentStep].Conclusion = model.StepStatusSuccess
|
rc.StepResults[rc.CurrentStep].Conclusion = model.StepStatusSuccess
|
||||||
|
@ -183,6 +190,22 @@ func isStepEnabled(ctx context.Context, expr string, step step, stage stepStage)
|
||||||
return runStep, nil
|
return runStep, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func isContinueOnError(ctx context.Context, expr string, step step, stage stepStage) (bool, error) {
|
||||||
|
// https://github.com/github/docs/blob/3ae84420bd10997bb5f35f629ebb7160fe776eae/content/actions/reference/workflow-syntax-for-github-actions.md?plain=true#L962
|
||||||
|
if len(strings.TrimSpace(expr)) == 0 {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
rc := step.getRunContext()
|
||||||
|
|
||||||
|
continueOnError, err := EvalBool(ctx, rc.NewStepExpressionEvaluator(ctx, step), expr, exprparser.DefaultStatusCheckNone)
|
||||||
|
if err != nil {
|
||||||
|
return false, fmt.Errorf(" \u274C Error in continue-on-error-expression: \"continue-on-error: %s\" (%s)", expr, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return continueOnError, nil
|
||||||
|
}
|
||||||
|
|
||||||
func mergeIntoMap(target *map[string]string, maps ...map[string]string) {
|
func mergeIntoMap(target *map[string]string, maps ...map[string]string) {
|
||||||
for _, m := range maps {
|
for _, m := range maps {
|
||||||
for k, v := range m {
|
for k, v := range m {
|
||||||
|
|
|
@ -276,3 +276,73 @@ func TestIsStepEnabled(t *testing.T) {
|
||||||
}
|
}
|
||||||
assertObject.True(isStepEnabled(context.Background(), step.getStepModel().If.Value, step, stepStageMain))
|
assertObject.True(isStepEnabled(context.Background(), step.getStepModel().If.Value, step, stepStageMain))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestIsContinueOnError(t *testing.T) {
|
||||||
|
createTestStep := func(t *testing.T, input string) step {
|
||||||
|
var step *model.Step
|
||||||
|
err := yaml.Unmarshal([]byte(input), &step)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
return &stepRun{
|
||||||
|
RunContext: &RunContext{
|
||||||
|
Config: &Config{
|
||||||
|
Workdir: ".",
|
||||||
|
Platforms: map[string]string{
|
||||||
|
"ubuntu-latest": "ubuntu-latest",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
StepResults: map[string]*model.StepResult{},
|
||||||
|
Env: map[string]string{},
|
||||||
|
Run: &model.Run{
|
||||||
|
JobID: "job1",
|
||||||
|
Workflow: &model.Workflow{
|
||||||
|
Name: "workflow1",
|
||||||
|
Jobs: map[string]*model.Job{
|
||||||
|
"job1": createJob(t, `runs-on: ubuntu-latest`, ""),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Step: step,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
log.SetLevel(log.DebugLevel)
|
||||||
|
assertObject := assert.New(t)
|
||||||
|
|
||||||
|
// absent
|
||||||
|
step := createTestStep(t, "name: test")
|
||||||
|
continueOnError, err := isContinueOnError(context.Background(), step.getStepModel().RawContinueOnError, step, stepStageMain)
|
||||||
|
assertObject.False(continueOnError)
|
||||||
|
assertObject.Nil(err)
|
||||||
|
|
||||||
|
// explcit true
|
||||||
|
step = createTestStep(t, "continue-on-error: true")
|
||||||
|
continueOnError, err = isContinueOnError(context.Background(), step.getStepModel().RawContinueOnError, step, stepStageMain)
|
||||||
|
assertObject.True(continueOnError)
|
||||||
|
assertObject.Nil(err)
|
||||||
|
|
||||||
|
// explicit false
|
||||||
|
step = createTestStep(t, "continue-on-error: false")
|
||||||
|
continueOnError, err = isContinueOnError(context.Background(), step.getStepModel().RawContinueOnError, step, stepStageMain)
|
||||||
|
assertObject.False(continueOnError)
|
||||||
|
assertObject.Nil(err)
|
||||||
|
|
||||||
|
// expression true
|
||||||
|
step = createTestStep(t, "continue-on-error: ${{ 'test' == 'test' }}")
|
||||||
|
continueOnError, err = isContinueOnError(context.Background(), step.getStepModel().RawContinueOnError, step, stepStageMain)
|
||||||
|
assertObject.True(continueOnError)
|
||||||
|
assertObject.Nil(err)
|
||||||
|
|
||||||
|
// expression false
|
||||||
|
step = createTestStep(t, "continue-on-error: ${{ 'test' != 'test' }}")
|
||||||
|
continueOnError, err = isContinueOnError(context.Background(), step.getStepModel().RawContinueOnError, step, stepStageMain)
|
||||||
|
assertObject.False(continueOnError)
|
||||||
|
assertObject.Nil(err)
|
||||||
|
|
||||||
|
// expression parse error
|
||||||
|
step = createTestStep(t, "continue-on-error: ${{ 'test' != test }}")
|
||||||
|
continueOnError, err = isContinueOnError(context.Background(), step.getStepModel().RawContinueOnError, step, stepStageMain)
|
||||||
|
assertObject.False(continueOnError)
|
||||||
|
assertObject.NotNil(err)
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue