mirror of
https://gitea.com/gitea/act_runner.git
synced 2026-07-22 02:37:45 +00:00
fix: accept natively typed boolean workflow inputs (#1087)
Gitea 1.27 resolves `workflow_call` inputs server-side and sends natively typed JSON values in `github.event.inputs`, so a `type: boolean` input now arrives as a real JSON boolean. The runner coerced booleans by comparing the `any` value against the string `"true"`, which a native bool never matches, so every boolean input evaluated to `false` — including when the callee relied on its default, since the server pre-fills defaults into the event payload and the string fallback is never reached. This accepts a native bool and keeps the string comparison as a fallback, since `workflow_dispatch` inputs are still strings and YAML defaults decode to strings; servers before 1.27 never put a native bool in the payload, so they take the exact same code path as before.
The same coercion is applied to `setupWorkflowInputs` (locally-called reusable workflows, `uses: ./.gitea/workflows/x.yml`), where a `type: boolean` input was previously a native bool when passed as `with: { flag: true }` but a string when interpolated or taken from `default:`. It is now always a bool, matching GitHub, whose `inputs` context "preserves Boolean values as Booleans instead of converting them to strings". **This is potentially breaking**: `inputs.flag == 'true'` now evaluates to `false` and must become `inputs.flag == true`. That pattern is already false on GitHub (a bool compared to a string coerces to `1 == NaN`), but it works on Gitea today, so I am happy to split this hunk into its own PR if you would rather keep this one backport-safe.
Fixes #1082
Reviewed-on: https://gitea.com/gitea/runner/pulls/1087
Reviewed-by: Zettat123 <39446+zettat123@noreply.gitea.com>
Co-authored-by: Nicolas <bircni@icloud.com>
This commit is contained in:
@@ -497,11 +497,7 @@ func getEvaluatorInputs(ctx context.Context, rc *RunContext, step step, ghc *mod
|
|||||||
if value == nil {
|
if value == nil {
|
||||||
value = v.Default
|
value = v.Default
|
||||||
}
|
}
|
||||||
if v.Type == "boolean" {
|
inputs[k] = coerceInputValue(value, v.Type)
|
||||||
inputs[k] = value == "true"
|
|
||||||
} else {
|
|
||||||
inputs[k] = value
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -514,17 +510,26 @@ func getEvaluatorInputs(ctx context.Context, rc *RunContext, step step, ghc *mod
|
|||||||
if value == nil {
|
if value == nil {
|
||||||
value = v.Default
|
value = v.Default
|
||||||
}
|
}
|
||||||
if v.Type == "boolean" {
|
inputs[k] = coerceInputValue(value, v.Type)
|
||||||
inputs[k] = value == "true"
|
|
||||||
} else {
|
|
||||||
inputs[k] = value
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return inputs
|
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) {
|
func setupWorkflowInputs(ctx context.Context, inputs *map[string]any, rc *RunContext) {
|
||||||
if rc.caller != nil {
|
if rc.caller != nil {
|
||||||
config := rc.Run.Workflow.WorkflowCallConfig()
|
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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,12 +6,14 @@ package runner
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"gitea.com/gitea/runner/act/exprparser"
|
"gitea.com/gitea/runner/act/exprparser"
|
||||||
"gitea.com/gitea/runner/act/model"
|
"gitea.com/gitea/runner/act/model"
|
||||||
|
|
||||||
assert "github.com/stretchr/testify/assert"
|
assert "github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
yaml "go.yaml.in/yaml/v4"
|
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"])
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"required": "required input",
|
"required": "required input",
|
||||||
"boolean": "true"
|
"boolean": true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user