mirror of
https://github.com/go-gitea/gitea.git
synced 2026-10-01 20:59:45 +09:00
0d09986790
Fixes https://github.com/go-gitea/gitea/issues/38705 Fixes https://gitea.com/gitea/runner/issues/1232 Jobs of a called workflow saw `gitea.event_name` as `workflow_call` and `gitea.event.inputs` replaced by the caller's `with:`, so a condition like `gitea.event_name == 'push'` never held in them, and a dispatched run's own inputs were lost there. They now keep the caller's trigger event and their `inputs` are the run's `workflow_dispatch` inputs overlaid with the caller's `with:`, as on GitHub. For example, with `workflow_dispatch` inputs `{target: prod, debug: true}` and caller's `with: {target: dev}`, the called workflow's `inputs` are `{target: dev, debug: true}`. A runner cannot resolve these inputs itself, so they are sent in a new `gitea_workflow_call` context entry, with the original event name and inputs for the runner to restore. For more details, see the runner PR: https://gitea.com/gitea/runner/pulls/1250 Co-authored-by: silverwind <me@silverwind.io>
146 lines
5.5 KiB
Go
146 lines
5.5 KiB
Go
// Copyright 2026 The Gitea Authors. All rights reserved.
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
package actions
|
|
|
|
import (
|
|
"testing"
|
|
|
|
actions_model "gitea.dev/models/actions"
|
|
"gitea.dev/models/db"
|
|
"gitea.dev/models/unittest"
|
|
actions_module "gitea.dev/modules/actions"
|
|
"gitea.dev/modules/json"
|
|
api "gitea.dev/modules/structs"
|
|
webhook_module "gitea.dev/modules/webhook"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func TestDispatchInputsForRunJobs(t *testing.T) {
|
|
// a child carries the callee's `on: workflow_call`, so only a top-level job answers for the run
|
|
run := &actions_model.ActionRun{Event: "workflow_dispatch", EventPayload: `{"inputs":{"deploy":"true"}}`}
|
|
job := &actions_model.ActionRunJob{
|
|
ID: 1, JobID: "deploy",
|
|
WorkflowPayload: []byte("on: {workflow_dispatch: {inputs: {deploy: {type: boolean}}}}\njobs:\n deploy:\n steps: [{run: echo}]\n"),
|
|
}
|
|
child := &actions_model.ActionRunJob{
|
|
ID: 2, JobID: "called", ParentJobID: job.ID,
|
|
WorkflowPayload: []byte("on: workflow_call\njobs:\n called:\n steps: [{run: echo}]\n"),
|
|
}
|
|
|
|
inputs, err := dispatchInputsForRunJobs(run, []*actions_model.ActionRunJob{child, job})
|
|
require.NoError(t, err)
|
|
assert.Equal(t, true, inputs["deploy"])
|
|
}
|
|
|
|
func TestReusableChildInputs(t *testing.T) {
|
|
require.NoError(t, unittest.PrepareTestDatabase())
|
|
ctx := t.Context()
|
|
|
|
const runID = 9801
|
|
insertJob := func(jobID string, parentID int64, payload, callPayload string) *actions_model.ActionRunJob {
|
|
job := &actions_model.ActionRunJob{RunID: runID, JobID: jobID, ParentJobID: parentID, WorkflowPayload: []byte(payload), CallPayload: callPayload}
|
|
require.NoError(t, db.Insert(ctx, job))
|
|
return job
|
|
}
|
|
caller := insertJob("caller", 0,
|
|
"on: {workflow_dispatch: {inputs: {flag: {type: boolean}, Shared: {type: string}}}}\njobs:\n caller:\n uses: ./.gitea/workflows/mid.yml\n",
|
|
`{"inputs":{"shared":"from-call","mid_only":"mid"}}`)
|
|
mid := insertJob("mid", caller.ID, "", `{"inputs":{"env":"leaf"}}`)
|
|
leaf := insertJob("leaf", mid.ID, "", "")
|
|
|
|
dispatchRun := &actions_model.ActionRun{ID: runID, Event: "workflow_dispatch", EventPayload: `{"inputs":{"flag":"true","Shared":"from-dispatch"}}`}
|
|
pushRun := &actions_model.ActionRun{ID: runID, Event: "push", EventPayload: `{}`}
|
|
|
|
t.Run("dispatch inputs overlaid case-insensitively with the caller's with", func(t *testing.T) {
|
|
inputs, err := getInputsForJob(ctx, dispatchRun, mid)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, map[string]any{"flag": true, "shared": "from-call", "mid_only": "mid"}, inputs)
|
|
})
|
|
|
|
t.Run("task context of a nested child keeps the older runners' form and carries the original event", func(t *testing.T) {
|
|
leaf.Run = dispatchRun
|
|
gitCtx := GiteaContext{
|
|
"event_name": "workflow_dispatch",
|
|
"event": map[string]any{"inputs": map[string]any{"flag": "true", "Shared": "from-dispatch"}},
|
|
}
|
|
require.NoError(t, setCalledWorkflowContext(ctx, leaf, gitCtx))
|
|
inputs := map[string]any{"flag": true, "Shared": "from-dispatch", "env": "leaf"}
|
|
assert.Equal(t, "workflow_call", gitCtx["event_name"])
|
|
assert.Equal(t, map[string]any{"inputs": inputs}, gitCtx["event"])
|
|
assert.Equal(t, map[string]any{
|
|
"original_event_name": "workflow_dispatch",
|
|
"original_event_inputs": map[string]any{"flag": "true", "Shared": "from-dispatch"},
|
|
"inputs": inputs,
|
|
}, gitCtx["gitea_workflow_call"])
|
|
})
|
|
|
|
t.Run("task context of a non-dispatch run", func(t *testing.T) {
|
|
mid.Run = pushRun
|
|
gitCtx := GiteaContext{"event_name": "push", "event": map[string]any{}}
|
|
require.NoError(t, setCalledWorkflowContext(ctx, mid, gitCtx))
|
|
inputs := map[string]any{"shared": "from-call", "mid_only": "mid"}
|
|
assert.Equal(t, "workflow_call", gitCtx["event_name"])
|
|
assert.Equal(t, map[string]any{"inputs": inputs}, gitCtx["event"])
|
|
assert.Equal(t, map[string]any{"original_event_name": "push", "inputs": inputs}, gitCtx["gitea_workflow_call"])
|
|
})
|
|
}
|
|
|
|
func TestPullRequestTargetBaseSHA(t *testing.T) {
|
|
prPayload := func(baseSHA string) string {
|
|
payload, err := json.Marshal(api.PullRequestPayload{
|
|
PullRequest: &api.PullRequest{
|
|
Base: &api.PRBranchInfo{Sha: baseSHA},
|
|
},
|
|
})
|
|
require.NoError(t, err)
|
|
return string(payload)
|
|
}
|
|
|
|
t.Run("pull_request_target with base SHA", func(t *testing.T) {
|
|
run := &actions_model.ActionRun{
|
|
Event: webhook_module.HookEventPullRequest,
|
|
TriggerEvent: actions_module.GithubEventPullRequestTarget,
|
|
EventPayload: prPayload("base-sha"),
|
|
}
|
|
got, ok := pullRequestTargetBaseSHA(run)
|
|
assert.True(t, ok)
|
|
assert.Equal(t, "base-sha", got)
|
|
})
|
|
|
|
t.Run("non pull_request_target trigger", func(t *testing.T) {
|
|
run := &actions_model.ActionRun{
|
|
Event: webhook_module.HookEventPullRequest,
|
|
TriggerEvent: actions_module.GithubEventPullRequest,
|
|
EventPayload: prPayload("base-sha"),
|
|
}
|
|
got, ok := pullRequestTargetBaseSHA(run)
|
|
assert.False(t, ok)
|
|
assert.Empty(t, got)
|
|
})
|
|
|
|
t.Run("missing base SHA", func(t *testing.T) {
|
|
run := &actions_model.ActionRun{
|
|
Event: webhook_module.HookEventPullRequest,
|
|
TriggerEvent: actions_module.GithubEventPullRequestTarget,
|
|
EventPayload: prPayload(""),
|
|
}
|
|
got, ok := pullRequestTargetBaseSHA(run)
|
|
assert.False(t, ok)
|
|
assert.Empty(t, got)
|
|
})
|
|
|
|
t.Run("invalid payload", func(t *testing.T) {
|
|
run := &actions_model.ActionRun{
|
|
Event: webhook_module.HookEventPullRequest,
|
|
TriggerEvent: actions_module.GithubEventPullRequestTarget,
|
|
EventPayload: "{",
|
|
}
|
|
got, ok := pullRequestTargetBaseSHA(run)
|
|
assert.False(t, ok)
|
|
assert.Empty(t, got)
|
|
})
|
|
}
|