fix: skip jobs whose if fails to evaluate, update actionslib (#1247)

Updates actionslib to https://gitea.com/gitea/actionslib/releases/tag/v1.2.1.

1. A job whose `if:` fails to evaluate, like `fromJSON` of an invalid job output, is skipped with the error in its log instead of failing, like on GitHub Actions.
1. Workflows the runner reads itself, like with `exec`, are rejected when an expression does not parse, like an unclosed `${{` or an unknown context, instead of failing later.
1. Task payloads from Gitea are read without that check, because they hold evaluated text that may contain `${{`.
1. `if: False`, `if: ~` and other boolean, null or number conditions work.

Gitea PR: https://github.com/go-gitea/gitea/pull/39358

Reviewed-on: https://gitea.com/gitea/runner/pulls/1247
Reviewed-by: bircni <bircni@icloud.com>
Co-authored-by: silverwind <me@silverwind.io>
This commit is contained in:
silverwind
2026-09-24 14:57:40 +00:00
committed by bircni
parent e0d00e543d
commit 98816a605c
5 changed files with 13 additions and 8 deletions

View File

@@ -1104,9 +1104,7 @@ func (rc *RunContext) Executor() (common.Executor, error) {
return func(ctx context.Context) error {
res, err := rc.isEnabled(ctx)
if err != nil {
// Record the failure so a job whose if-expression fails to evaluate
// gets a result (and therefore a stop time) instead of being left
// unfinished. rc.caller is only set for reusable workflows.
// Record the failure so a job that cannot start gets a stop time. rc.caller is only set for reusable workflows.
rc.result("failure")
if rc.caller != nil { // For Gitea
rc.caller.setReusedWorkflowJobResult(rc.Run.JobID, "failure")
@@ -1197,7 +1195,7 @@ func (rc *RunContext) isEnabled(ctx context.Context) (bool, error) {
jobType, jobTypeErr := job.Type()
if runJobErr != nil {
return false, fmt.Errorf("if-expression %q evaluation failed: %s", job.If.Value, runJobErr)
l.Errorf("Error when evaluating 'if' for job '%s'. %v", rc.Run.JobID, runJobErr)
}
if jobType == model.JobTypeInvalid {

View File

@@ -1141,6 +1141,13 @@ if: always()`, ""),
if: false`, ""),
})
assertObject.False(rc.isEnabled(context.Background()))
rc = createIfTestRunContext(map[string]*model.Job{
"job1": createJob(t, `if: ${{ fromJSON('not-json') }}`, ""),
})
enabled, err := rc.isEnabled(context.Background())
require.NoError(t, err)
assertObject.False(enabled)
}
func TestRunContextGetEnv(t *testing.T) {

2
go.mod
View File

@@ -7,7 +7,7 @@ toolchain go1.27.1
require (
connectrpc.com/connect v1.21.0
dario.cat/mergo v1.0.2
gitea.dev/actionslib v1.2.0
gitea.dev/actionslib v1.2.1
github.com/avast/retry-go/v5 v5.0.0
github.com/containerd/errdefs v1.0.0
github.com/creack/pty v1.1.24

4
go.sum
View File

@@ -4,8 +4,8 @@ cyphar.com/go-pathrs v0.2.5 h1:SnX9FBvnoyn3lUs1dkMgZ52bAETpirNu3FTRh5HlRik=
cyphar.com/go-pathrs v0.2.5/go.mod h1:y8f1EMG7r+hCuFf/rXsKqMJrJAUoADZGNh5/vZPKcGc=
dario.cat/mergo v1.0.2 h1:85+piFYR1tMbRrLcDwR18y4UKJ3aH1Tbzi24VRW1TK8=
dario.cat/mergo v1.0.2/go.mod h1:E/hbnu0NxMFBjpMIE34DRGLWqDy0g5FuKDhCb31ngxA=
gitea.dev/actionslib v1.2.0 h1:R/PBu9DwD04tCd5rjAWT9dAa+S6GOk1XnXCC3T2Rrnw=
gitea.dev/actionslib v1.2.0/go.mod h1:1+gqOKGSEPn2IFHgY8S3GC5Ld+Hn8jF3OxiFHQ/fpx4=
gitea.dev/actionslib v1.2.1 h1:GL//K/0zIZV6h1OOksv1awvFVg0rg7fug2xWcG7mkG0=
gitea.dev/actionslib v1.2.1/go.mod h1:1+gqOKGSEPn2IFHgY8S3GC5Ld+Hn8jF3OxiFHQ/fpx4=
github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6 h1:He8afgbRMd7mFxO99hRNu+6tazq8nFF9lIwo9JFroBk=
github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8=
github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY=

View File

@@ -15,7 +15,7 @@ import (
)
func generateWorkflow(task *runnerv1.Task) (*model.Workflow, string, error) {
workflow, err := model.ReadWorkflow(bytes.NewReader(task.WorkflowPayload))
workflow, err := model.ReadWorkflowPayload(bytes.NewReader(task.WorkflowPayload))
if err != nil {
return nil, "", err
}