Skip to content

Commit cdc0733

Browse files
authored
Use inputs context when parsing workflows (#35590)
Depends on [gitea/act#143](https://gitea.com/gitea/act/pulls/143) The [`inputs` context](https://docs.github.com/en/actions/reference/workflows-and-actions/contexts#inputs-context) is used when parsing workflows so that `run-name` like `run-name: Deploy to ${{ inputs.deploy_target }}` can be parsed correctly.
1 parent 0a0baeb commit cdc0733

File tree

5 files changed

+121
-18
lines changed

5 files changed

+121
-18
lines changed

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -295,7 +295,7 @@ replace github.com/jaytaylor/html2text => github.com/Necoro/html2text v0.0.0-202
295295

296296
replace github.com/hashicorp/go-version => github.com/6543/go-version v1.3.1
297297

298-
replace github.com/nektos/act => gitea.com/gitea/act v0.261.6
298+
replace github.com/nektos/act => gitea.com/gitea/act v0.261.7-0.20251003180512-ac6e4b751763
299299

300300
// TODO: the only difference is in `PutObject`: the fork doesn't use `NewVerifyingReader(r, sha256.New(), oid, expectedSize)`, need to figure out why
301301
replace github.com/charmbracelet/git-lfs-transfer => gitea.com/gitea/git-lfs-transfer v0.2.0

go.sum

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,8 @@ dario.cat/mergo v1.0.2/go.mod h1:E/hbnu0NxMFBjpMIE34DRGLWqDy0g5FuKDhCb31ngxA=
3131
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
3232
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
3333
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
34-
gitea.com/gitea/act v0.261.6 h1:CjZwKOyejonNFDmsXOw3wGm5Vet573hHM6VMLsxtvPY=
35-
gitea.com/gitea/act v0.261.6/go.mod h1:Pg5C9kQY1CEA3QjthjhlrqOC/QOT5NyWNjOjRHw23Ok=
34+
gitea.com/gitea/act v0.261.7-0.20251003180512-ac6e4b751763 h1:ohdxegvslDEllZmRNDqpKun6L4Oq81jNdEDtGgHEV2c=
35+
gitea.com/gitea/act v0.261.7-0.20251003180512-ac6e4b751763/go.mod h1:Pg5C9kQY1CEA3QjthjhlrqOC/QOT5NyWNjOjRHw23Ok=
3636
gitea.com/gitea/git-lfs-transfer v0.2.0 h1:baHaNoBSRaeq/xKayEXwiDQtlIjps4Ac/Ll4KqLMB40=
3737
gitea.com/gitea/git-lfs-transfer v0.2.0/go.mod h1:UrXUCm3xLQkq15fu7qlXHUMlrhdlXHoi13KH2Dfiits=
3838
gitea.com/gitea/go-xsd-duration v0.0.0-20220703122237-02e73435a078 h1:BAFmdZpRW7zMQZQDClaCWobRj9uL1MR3MzpCVJvc5s4=

services/actions/workflow.go

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import (
2626

2727
"github.com/nektos/act/pkg/jobparser"
2828
"github.com/nektos/act/pkg/model"
29+
"gopkg.in/yaml.v3"
2930
)
3031

3132
func EnableOrDisableWorkflow(ctx *context.APIContext, workflowID string, isEnable bool) error {
@@ -136,9 +137,24 @@ func DispatchActionWorkflow(ctx reqctx.RequestContext, doer *user_model.User, re
136137
return err
137138
}
138139

140+
singleWorkflow := &jobparser.SingleWorkflow{}
141+
if err := yaml.Unmarshal(content, singleWorkflow); err != nil {
142+
return fmt.Errorf("failed to unmarshal workflow content: %w", err)
143+
}
144+
// get inputs from post
145+
workflow := &model.Workflow{
146+
RawOn: singleWorkflow.RawOn,
147+
}
148+
inputsWithDefaults := make(map[string]any)
149+
if workflowDispatch := workflow.WorkflowDispatchConfig(); workflowDispatch != nil {
150+
if err = processInputs(workflowDispatch, inputsWithDefaults); err != nil {
151+
return err
152+
}
153+
}
154+
139155
giteaCtx := GenerateGiteaContext(run, nil)
140156

141-
workflows, err = jobparser.Parse(content, jobparser.WithGitContext(giteaCtx.ToGitHubContext()))
157+
workflows, err = jobparser.Parse(content, jobparser.WithGitContext(giteaCtx.ToGitHubContext()), jobparser.WithInputs(inputsWithDefaults))
142158
if err != nil {
143159
return err
144160
}
@@ -154,17 +170,6 @@ func DispatchActionWorkflow(ctx reqctx.RequestContext, doer *user_model.User, re
154170
)
155171
}
156172

157-
// get inputs from post
158-
workflow := &model.Workflow{
159-
RawOn: workflows[0].RawOn,
160-
}
161-
inputsWithDefaults := make(map[string]any)
162-
if workflowDispatch := workflow.WorkflowDispatchConfig(); workflowDispatch != nil {
163-
if err = processInputs(workflowDispatch, inputsWithDefaults); err != nil {
164-
return err
165-
}
166-
}
167-
168173
// ctx.Req.PostForm -> WorkflowDispatchPayload.Inputs -> ActionRun.EventPayload -> runner: ghc.Event
169174
// https://docs.github.com/en/actions/learn-github-actions/contexts#github-context
170175
// https://docs.github.com/en/webhooks/webhook-events-and-payloads#workflow_dispatch
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
// Copyright 2025 The Gitea Authors. All rights reserved.
2+
// SPDX-License-Identifier: MIT
3+
4+
package integration
5+
6+
import (
7+
"fmt"
8+
"net/http"
9+
"net/url"
10+
"testing"
11+
12+
actions_model "code.gitea.io/gitea/models/actions"
13+
auth_model "code.gitea.io/gitea/models/auth"
14+
repo_model "code.gitea.io/gitea/models/repo"
15+
"code.gitea.io/gitea/models/unittest"
16+
user_model "code.gitea.io/gitea/models/user"
17+
18+
"github.com/stretchr/testify/assert"
19+
)
20+
21+
func TestWorkflowWithInputsContext(t *testing.T) {
22+
onGiteaRun(t, func(t *testing.T, u *url.URL) {
23+
user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
24+
session := loginUser(t, user2.Name)
25+
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository, auth_model.AccessTokenScopeWriteUser)
26+
27+
apiRepo := createActionsTestRepo(t, token, "actions-inputs-context", false)
28+
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: apiRepo.ID})
29+
httpContext := NewAPITestContext(t, user2.Name, repo.Name, auth_model.AccessTokenScopeWriteRepository)
30+
defer doAPIDeleteRepository(httpContext)(t)
31+
32+
wRunner := newMockRunner()
33+
wRunner.registerAsRepoRunner(t, user2.Name, repo.Name, "windows-runner", []string{"windows-runner"}, false)
34+
lRunner := newMockRunner()
35+
lRunner.registerAsRepoRunner(t, user2.Name, repo.Name, "linux-runner", []string{"linux-runner"}, false)
36+
37+
wf1TreePath := ".gitea/workflows/test-inputs-context.yml"
38+
wf1FileContent := `name: Test Inputs Context
39+
on:
40+
workflow_dispatch:
41+
inputs:
42+
os:
43+
description: 'OS'
44+
required: true
45+
type: choice
46+
options:
47+
- linux
48+
- windows
49+
50+
run-name: Build APP on ${{ inputs.os }}
51+
52+
jobs:
53+
build:
54+
runs-on: ${{ inputs.os }}-runner
55+
steps:
56+
- run: echo 'Start building APP'
57+
`
58+
59+
opts1 := getWorkflowCreateFileOptions(user2, repo.DefaultBranch, "create %s"+wf1TreePath, wf1FileContent)
60+
createWorkflowFile(t, token, user2.Name, repo.Name, wf1TreePath, opts1)
61+
62+
// run the workflow with os=windows
63+
urlStr := fmt.Sprintf("/%s/%s/actions/run?workflow=%s", user2.Name, repo.Name, "test-inputs-context.yml")
64+
req := NewRequestWithValues(t, "POST", urlStr, map[string]string{
65+
"_csrf": GetUserCSRFToken(t, session),
66+
"ref": "refs/heads/main",
67+
"os": "windows",
68+
})
69+
session.MakeRequest(t, req, http.StatusSeeOther)
70+
71+
// linux-runner cannot fetch the task
72+
lRunner.fetchNoTask(t)
73+
74+
task := wRunner.fetchTask(t)
75+
_, _, run := getTaskAndJobAndRunByTaskID(t, task.Id)
76+
assert.Equal(t, "Build APP on windows", run.Title)
77+
})
78+
}
79+
80+
func getTaskAndJobAndRunByTaskID(t *testing.T, taskID int64) (*actions_model.ActionTask, *actions_model.ActionRunJob, *actions_model.ActionRun) {
81+
actionTask := unittest.AssertExistsAndLoadBean(t, &actions_model.ActionTask{ID: taskID})
82+
actionRunJob := unittest.AssertExistsAndLoadBean(t, &actions_model.ActionRunJob{ID: actionTask.JobID})
83+
actionRun := unittest.AssertExistsAndLoadBean(t, &actions_model.ActionRun{ID: actionRunJob.RunID})
84+
return actionTask, actionRunJob, actionRun
85+
}

tests/integration/actions_runner_test.go

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,20 @@ func (r *mockRunner) registerAsRepoRunner(t *testing.T, ownerName, repoName, run
9393
}
9494

9595
func (r *mockRunner) fetchTask(t *testing.T, timeout ...time.Duration) *runnerv1.Task {
96-
fetchTimeout := 10 * time.Second
96+
task := r.tryFetchTask(t, timeout...)
97+
assert.NotNil(t, task, "failed to fetch a task")
98+
return task
99+
}
100+
101+
func (r *mockRunner) fetchNoTask(t *testing.T, timeout ...time.Duration) {
102+
task := r.tryFetchTask(t, timeout...)
103+
assert.Nil(t, task, "a task is fetched")
104+
}
105+
106+
const defaultFetchTaskTimeout = 1 * time.Second
107+
108+
func (r *mockRunner) tryFetchTask(t *testing.T, timeout ...time.Duration) *runnerv1.Task {
109+
fetchTimeout := defaultFetchTaskTimeout
97110
if len(timeout) > 0 {
98111
fetchTimeout = timeout[0]
99112
}
@@ -108,9 +121,9 @@ func (r *mockRunner) fetchTask(t *testing.T, timeout ...time.Duration) *runnerv1
108121
task = resp.Msg.Task
109122
break
110123
}
111-
time.Sleep(time.Second)
124+
time.Sleep(200 * time.Millisecond)
112125
}
113-
assert.NotNil(t, task, "failed to fetch a task")
126+
114127
return task
115128
}
116129

0 commit comments

Comments
 (0)