diff --git a/go.mod b/go.mod index f32c3e08ef436..c5c72083b4f06 100644 --- a/go.mod +++ b/go.mod @@ -295,7 +295,7 @@ replace github.com/jaytaylor/html2text => github.com/Necoro/html2text v0.0.0-202 replace github.com/hashicorp/go-version => github.com/6543/go-version v1.3.1 -replace github.com/nektos/act => gitea.com/gitea/act v0.261.6 +replace github.com/nektos/act => gitea.com/gitea/act v0.261.7-0.20251003180512-ac6e4b751763 // TODO: the only difference is in `PutObject`: the fork doesn't use `NewVerifyingReader(r, sha256.New(), oid, expectedSize)`, need to figure out why replace github.com/charmbracelet/git-lfs-transfer => gitea.com/gitea/git-lfs-transfer v0.2.0 diff --git a/go.sum b/go.sum index 1853693e90dcf..632209360b039 100644 --- a/go.sum +++ b/go.sum @@ -31,8 +31,8 @@ dario.cat/mergo v1.0.2/go.mod h1:E/hbnu0NxMFBjpMIE34DRGLWqDy0g5FuKDhCb31ngxA= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= -gitea.com/gitea/act v0.261.6 h1:CjZwKOyejonNFDmsXOw3wGm5Vet573hHM6VMLsxtvPY= -gitea.com/gitea/act v0.261.6/go.mod h1:Pg5C9kQY1CEA3QjthjhlrqOC/QOT5NyWNjOjRHw23Ok= +gitea.com/gitea/act v0.261.7-0.20251003180512-ac6e4b751763 h1:ohdxegvslDEllZmRNDqpKun6L4Oq81jNdEDtGgHEV2c= +gitea.com/gitea/act v0.261.7-0.20251003180512-ac6e4b751763/go.mod h1:Pg5C9kQY1CEA3QjthjhlrqOC/QOT5NyWNjOjRHw23Ok= gitea.com/gitea/git-lfs-transfer v0.2.0 h1:baHaNoBSRaeq/xKayEXwiDQtlIjps4Ac/Ll4KqLMB40= gitea.com/gitea/git-lfs-transfer v0.2.0/go.mod h1:UrXUCm3xLQkq15fu7qlXHUMlrhdlXHoi13KH2Dfiits= gitea.com/gitea/go-xsd-duration v0.0.0-20220703122237-02e73435a078 h1:BAFmdZpRW7zMQZQDClaCWobRj9uL1MR3MzpCVJvc5s4= diff --git a/services/actions/workflow.go b/services/actions/workflow.go index 233e22b5ddfe2..40b34194e971b 100644 --- a/services/actions/workflow.go +++ b/services/actions/workflow.go @@ -26,6 +26,7 @@ import ( "github.com/nektos/act/pkg/jobparser" "github.com/nektos/act/pkg/model" + "gopkg.in/yaml.v3" ) func EnableOrDisableWorkflow(ctx *context.APIContext, workflowID string, isEnable bool) error { @@ -136,9 +137,24 @@ func DispatchActionWorkflow(ctx reqctx.RequestContext, doer *user_model.User, re return err } + singleWorkflow := &jobparser.SingleWorkflow{} + if err := yaml.Unmarshal(content, singleWorkflow); err != nil { + return fmt.Errorf("failed to unmarshal workflow content: %w", err) + } + // get inputs from post + workflow := &model.Workflow{ + RawOn: singleWorkflow.RawOn, + } + inputsWithDefaults := make(map[string]any) + if workflowDispatch := workflow.WorkflowDispatchConfig(); workflowDispatch != nil { + if err = processInputs(workflowDispatch, inputsWithDefaults); err != nil { + return err + } + } + giteaCtx := GenerateGiteaContext(run, nil) - workflows, err = jobparser.Parse(content, jobparser.WithGitContext(giteaCtx.ToGitHubContext())) + workflows, err = jobparser.Parse(content, jobparser.WithGitContext(giteaCtx.ToGitHubContext()), jobparser.WithInputs(inputsWithDefaults)) if err != nil { return err } @@ -154,17 +170,6 @@ func DispatchActionWorkflow(ctx reqctx.RequestContext, doer *user_model.User, re ) } - // get inputs from post - workflow := &model.Workflow{ - RawOn: workflows[0].RawOn, - } - inputsWithDefaults := make(map[string]any) - if workflowDispatch := workflow.WorkflowDispatchConfig(); workflowDispatch != nil { - if err = processInputs(workflowDispatch, inputsWithDefaults); err != nil { - return err - } - } - // ctx.Req.PostForm -> WorkflowDispatchPayload.Inputs -> ActionRun.EventPayload -> runner: ghc.Event // https://docs.github.com/en/actions/learn-github-actions/contexts#github-context // https://docs.github.com/en/webhooks/webhook-events-and-payloads#workflow_dispatch diff --git a/tests/integration/actions_inputs_test.go b/tests/integration/actions_inputs_test.go new file mode 100644 index 0000000000000..25ed9f71f8aac --- /dev/null +++ b/tests/integration/actions_inputs_test.go @@ -0,0 +1,85 @@ +// Copyright 2025 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package integration + +import ( + "fmt" + "net/http" + "net/url" + "testing" + + actions_model "code.gitea.io/gitea/models/actions" + auth_model "code.gitea.io/gitea/models/auth" + repo_model "code.gitea.io/gitea/models/repo" + "code.gitea.io/gitea/models/unittest" + user_model "code.gitea.io/gitea/models/user" + + "github.com/stretchr/testify/assert" +) + +func TestWorkflowWithInputsContext(t *testing.T) { + onGiteaRun(t, func(t *testing.T, u *url.URL) { + user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) + session := loginUser(t, user2.Name) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository, auth_model.AccessTokenScopeWriteUser) + + apiRepo := createActionsTestRepo(t, token, "actions-inputs-context", false) + repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: apiRepo.ID}) + httpContext := NewAPITestContext(t, user2.Name, repo.Name, auth_model.AccessTokenScopeWriteRepository) + defer doAPIDeleteRepository(httpContext)(t) + + wRunner := newMockRunner() + wRunner.registerAsRepoRunner(t, user2.Name, repo.Name, "windows-runner", []string{"windows-runner"}, false) + lRunner := newMockRunner() + lRunner.registerAsRepoRunner(t, user2.Name, repo.Name, "linux-runner", []string{"linux-runner"}, false) + + wf1TreePath := ".gitea/workflows/test-inputs-context.yml" + wf1FileContent := `name: Test Inputs Context +on: + workflow_dispatch: + inputs: + os: + description: 'OS' + required: true + type: choice + options: + - linux + - windows + +run-name: Build APP on ${{ inputs.os }} + +jobs: + build: + runs-on: ${{ inputs.os }}-runner + steps: + - run: echo 'Start building APP' +` + + opts1 := getWorkflowCreateFileOptions(user2, repo.DefaultBranch, "create %s"+wf1TreePath, wf1FileContent) + createWorkflowFile(t, token, user2.Name, repo.Name, wf1TreePath, opts1) + + // run the workflow with os=windows + urlStr := fmt.Sprintf("/%s/%s/actions/run?workflow=%s", user2.Name, repo.Name, "test-inputs-context.yml") + req := NewRequestWithValues(t, "POST", urlStr, map[string]string{ + "_csrf": GetUserCSRFToken(t, session), + "ref": "refs/heads/main", + "os": "windows", + }) + session.MakeRequest(t, req, http.StatusSeeOther) + + // linux-runner cannot fetch the task + lRunner.fetchNoTask(t) + + task := wRunner.fetchTask(t) + _, _, run := getTaskAndJobAndRunByTaskID(t, task.Id) + assert.Equal(t, "Build APP on windows", run.Title) + }) +} + +func getTaskAndJobAndRunByTaskID(t *testing.T, taskID int64) (*actions_model.ActionTask, *actions_model.ActionRunJob, *actions_model.ActionRun) { + actionTask := unittest.AssertExistsAndLoadBean(t, &actions_model.ActionTask{ID: taskID}) + actionRunJob := unittest.AssertExistsAndLoadBean(t, &actions_model.ActionRunJob{ID: actionTask.JobID}) + actionRun := unittest.AssertExistsAndLoadBean(t, &actions_model.ActionRun{ID: actionRunJob.RunID}) + return actionTask, actionRunJob, actionRun +} diff --git a/tests/integration/actions_runner_test.go b/tests/integration/actions_runner_test.go index 6cc5a10e0f503..c0ed1e6644dfc 100644 --- a/tests/integration/actions_runner_test.go +++ b/tests/integration/actions_runner_test.go @@ -93,7 +93,20 @@ func (r *mockRunner) registerAsRepoRunner(t *testing.T, ownerName, repoName, run } func (r *mockRunner) fetchTask(t *testing.T, timeout ...time.Duration) *runnerv1.Task { - fetchTimeout := 10 * time.Second + task := r.tryFetchTask(t, timeout...) + assert.NotNil(t, task, "failed to fetch a task") + return task +} + +func (r *mockRunner) fetchNoTask(t *testing.T, timeout ...time.Duration) { + task := r.tryFetchTask(t, timeout...) + assert.Nil(t, task, "a task is fetched") +} + +const defaultFetchTaskTimeout = 1 * time.Second + +func (r *mockRunner) tryFetchTask(t *testing.T, timeout ...time.Duration) *runnerv1.Task { + fetchTimeout := defaultFetchTaskTimeout if len(timeout) > 0 { fetchTimeout = timeout[0] } @@ -108,9 +121,9 @@ func (r *mockRunner) fetchTask(t *testing.T, timeout ...time.Duration) *runnerv1 task = resp.Msg.Task break } - time.Sleep(time.Second) + time.Sleep(200 * time.Millisecond) } - assert.NotNil(t, task, "failed to fetch a task") + return task }