diff --git a/act/runner/expression.go b/act/runner/expression.go index c64f921b..060ac5fd 100644 --- a/act/runner/expression.go +++ b/act/runner/expression.go @@ -497,11 +497,7 @@ func getEvaluatorInputs(ctx context.Context, rc *RunContext, step step, ghc *mod if value == nil { value = v.Default } - if v.Type == "boolean" { - inputs[k] = value == "true" - } else { - inputs[k] = value - } + inputs[k] = coerceInputValue(value, v.Type) } } } @@ -514,17 +510,26 @@ func getEvaluatorInputs(ctx context.Context, rc *RunContext, step step, ghc *mod if value == nil { value = v.Default } - if v.Type == "boolean" { - inputs[k] = value == "true" - } else { - inputs[k] = value - } + inputs[k] = coerceInputValue(value, v.Type) } } } return inputs } +// coerceInputValue converts an input value to the type declared by the workflow. +// The event payload carries natively typed JSON values on newer Gitea versions, +// while defaults and older servers provide strings. +func coerceInputValue(value any, inputType string) any { + if inputType != "boolean" { + return value + } + if b, ok := value.(bool); ok { + return b + } + return value == "true" +} + func setupWorkflowInputs(ctx context.Context, inputs *map[string]any, rc *RunContext) { if rc.caller != nil { config := rc.Run.Workflow.WorkflowCallConfig() @@ -548,7 +553,7 @@ func setupWorkflowInputs(ctx context.Context, inputs *map[string]any, rc *RunCon } } - (*inputs)[name] = value + (*inputs)[name] = coerceInputValue(value, input.Type) } } } diff --git a/act/runner/expression_test.go b/act/runner/expression_test.go index 59fc704b..e988e086 100644 --- a/act/runner/expression_test.go +++ b/act/runner/expression_test.go @@ -6,12 +6,14 @@ package runner import ( "context" + "strings" "testing" "gitea.com/gitea/runner/act/exprparser" "gitea.com/gitea/runner/act/model" assert "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" yaml "go.yaml.in/yaml/v4" ) @@ -321,3 +323,82 @@ func TestRewriteSubExpressionForceFormat(t *testing.T) { }) } } + +func TestGetEvaluatorInputsBoolean(t *testing.T) { + workflows := map[string]string{ + "workflow_call": ` +on: + workflow_call: + inputs: + flag: + type: boolean + default: true + name: + type: string + default: gitea +`, + "workflow_dispatch": ` +on: + workflow_dispatch: + inputs: + flag: + type: boolean + default: true + name: + type: string + default: gitea +`, + } + + tables := []struct { + name string + event map[string]any + flag any + }{ + { + // Gitea >= 1.27 resolves the inputs server-side and sends native JSON types + name: "native bool true", + event: map[string]any{"inputs": map[string]any{"flag": true}}, + flag: true, + }, + { + name: "native bool false", + event: map[string]any{"inputs": map[string]any{"flag": false}}, + flag: false, + }, + { + name: "string true", + event: map[string]any{"inputs": map[string]any{"flag": "true"}}, + flag: true, + }, + { + name: "string false", + event: map[string]any{"inputs": map[string]any{"flag": "false"}}, + flag: false, + }, + { + name: "default is used when the event carries no inputs", + event: map[string]any{}, + flag: true, + }, + } + + for eventName, workflow := range workflows { + for _, table := range tables { + t.Run(eventName+"/"+table.name, func(t *testing.T) { + wf, err := model.ReadWorkflow(strings.NewReader(workflow)) + require.NoError(t, err) + + rc := &RunContext{ + Config: &Config{Workdir: "."}, + Run: &model.Run{JobID: "job1", Workflow: wf}, + } + ghc := &model.GithubContext{EventName: eventName, Event: table.event} + + inputs := getEvaluatorInputs(context.Background(), rc, nil, ghc) + assert.Equal(t, table.flag, inputs["flag"]) + assert.Equal(t, "gitea", inputs["name"]) + }) + } + } +} diff --git a/act/runner/testdata/workflow_call_inputs/event.json b/act/runner/testdata/workflow_call_inputs/event.json index d3ecab10..48471c49 100644 --- a/act/runner/testdata/workflow_call_inputs/event.json +++ b/act/runner/testdata/workflow_call_inputs/event.json @@ -1,6 +1,6 @@ { "inputs": { "required": "required input", - "boolean": "true" + "boolean": true } }