mirror of
https://github.com/go-gitea/gitea.git
synced 2026-07-28 09:15:18 +02:00
Backport #38472 by @bircni A `workflow_dispatch` job that has `needs:` **and** an `if:` comparing a boolean input against a boolean literal never runs — it stays `Blocked` forever even though its needs succeed. Minimal repro: ```yaml on: workflow_dispatch: inputs: deploy: type: boolean default: true jobs: build: runs-on: ubuntu-latest steps: [{ run: echo build }] deploy: needs: build if: ${{ inputs.deploy == true }} # never true on Gitea runs-on: ubuntu-latest steps: [{ run: echo deploy }] ``` On GitHub this runs; on Gitea `deploy` is stuck. Jobs **without** `needs` are unaffected, which makes it look like a matrix/`needs` bug — it isn't. ## Root cause `workflow_dispatch` stores boolean inputs as the strings `"true"`/`"false"` (`ctx.FormBool` → `strconv.FormatBool` in the web path, plain strings in the API path). Since #37478 (shipped in **v1.27.0**), `evaluateJobIf` runs **server-side** as part of the job-emitter resolver passes. For a `needs`-gated job the server therefore evaluates `inputs.deploy == true` with `inputs.deploy` being the string `"true"`; comparing a string to a boolean coerces to `NaN == 1` → `false`, so the job is never dispatched. Jobs without `needs` skip this server-side gate and are evaluated by the runner, which coerces the string to a real bool — that's why they keep working, and why the same input yields opposite results in the two paths. ## Fix Normalize `type: boolean` dispatch inputs to native JSON booleans in the shared `DispatchActionWorkflow` path, so it covers both the web and API entry points in one place. The coerced value flows into both the run's `EventPayload` (read back by the server-side `if` evaluation and by the runner) and the creation-time parse, so all consumers agree. This matches GitHub, whose `inputs` context "preserves Boolean values as Booleans instead of converting them to strings", and mirrors the native JSON types Gitea already sends for `workflow_call`. Only booleans are coerced; other input types are left untouched, and a value that is already a bool (JSON API request) passes through. Note: this makes dispatch payloads carry native booleans, which the runner consumes correctly as of gitea/runner#1087 (it accepts a native bool and keeps the string fallback) — the same expectation `workflow_call` already relies on. Fixes https://github.com/go-gitea/gitea/issues/38466 Co-authored-by: bircni <bircni@icloud.com>
42 lines
1.2 KiB
Go
42 lines
1.2 KiB
Go
// Copyright 2026 The Gitea Authors. All rights reserved.
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
package actions
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"gitea.com/gitea/runner/act/model"
|
|
"github.com/stretchr/testify/assert"
|
|
)
|
|
|
|
func TestCoerceDispatchInputTypes(t *testing.T) {
|
|
dispatch := &model.WorkflowDispatch{
|
|
Inputs: map[string]model.WorkflowDispatchInput{
|
|
"build_server": {Type: "boolean"},
|
|
"dry_run": {Type: "boolean"},
|
|
"already_bool": {Type: "boolean"},
|
|
"version": {Type: "string"},
|
|
},
|
|
}
|
|
|
|
inputs := map[string]any{
|
|
// dispatch callbacks fill booleans as strconv.FormatBool(...) strings
|
|
"build_server": "true",
|
|
"dry_run": "false",
|
|
// already-native booleans are passed through unchanged (coercion is idempotent)
|
|
"already_bool": true,
|
|
// non-boolean inputs must be left untouched
|
|
"version": "1.2.3",
|
|
}
|
|
|
|
coerceDispatchInputTypes(dispatch, inputs)
|
|
|
|
// Regression: without coercion these stay strings, and a server-side needs-gated
|
|
// job `if: inputs.build_server == true` never matches, leaving the job blocked.
|
|
assert.Equal(t, true, inputs["build_server"])
|
|
assert.Equal(t, false, inputs["dry_run"])
|
|
assert.Equal(t, true, inputs["already_bool"])
|
|
assert.Equal(t, "1.2.3", inputs["version"])
|
|
}
|