diff --git a/routers/api/v1/repo/action.go b/routers/api/v1/repo/action.go index ca72b52a22..169ed4b68e 100644 --- a/routers/api/v1/repo/action.go +++ b/routers/api/v1/repo/action.go @@ -1091,6 +1091,8 @@ func ActionsDispatchWorkflow(ctx *context.APIContext) { ctx.APIError(http.StatusNotFound, err) } else if errors.Is(err, util.ErrPermissionDenied) { ctx.APIError(http.StatusForbidden, err) + } else if errors.Is(err, util.ErrInvalidArgument) { + ctx.APIError(http.StatusUnprocessableEntity, err) } else { ctx.APIErrorInternal(err) } diff --git a/services/actions/workflow.go b/services/actions/workflow.go index 45f15feb59..a748b0859c 100644 --- a/services/actions/workflow.go +++ b/services/actions/workflow.go @@ -140,11 +140,17 @@ func DispatchActionWorkflow(ctx reqctx.RequestContext, doer *user_model.User, re workflow := &model.Workflow{ RawOn: singleWorkflow.RawOn, } + workflowDispatch := workflow.WorkflowDispatchConfig() + if workflowDispatch == nil { + return 0, util.ErrorWrapTranslatable( + util.NewInvalidArgumentErrorf("workflow %q has no workflow_dispatch event trigger", workflowID), + "actions.workflow.has_no_workflow_dispatch", workflowID, + ) + } + inputsWithDefaults := make(map[string]any) - if workflowDispatch := workflow.WorkflowDispatchConfig(); workflowDispatch != nil { - if err = processInputs(workflowDispatch, inputsWithDefaults); err != nil { - return 0, err - } + if err = processInputs(workflowDispatch, inputsWithDefaults); err != nil { + return 0, err } // ctx.Req.PostForm -> WorkflowDispatchPayload.Inputs -> ActionRun.EventPayload -> runner: ghc.Event diff --git a/tests/integration/actions_trigger_test.go b/tests/integration/actions_trigger_test.go index 197abe54d0..cd87bc8b1a 100644 --- a/tests/integration/actions_trigger_test.go +++ b/tests/integration/actions_trigger_test.go @@ -931,6 +931,76 @@ jobs: }) } +func TestWorkflowDispatchPublicApiRequiresWorkflowDispatchTrigger(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) + + repo, err := repo_service.CreateRepository(t.Context(), user2, user2, repo_service.CreateRepoOptions{ + Name: "workflow-dispatch-requires-trigger", + Description: "test workflow dispatch requires workflow_dispatch", + AutoInit: true, + Gitignores: "Go", + License: "MIT", + Readme: "Default", + DefaultBranch: "main", + IsPrivate: false, + }) + require.NoError(t, err) + require.NotNil(t, repo) + + addWorkflowToBaseResp, err := files_service.ChangeRepoFiles(t.Context(), repo, user2, &files_service.ChangeRepoFilesOptions{ + Files: []*files_service.ChangeRepoFile{ + { + Operation: "create", + TreePath: ".gitea/workflows/push-only.yml", + ContentReader: strings.NewReader(` +on: + push: +jobs: + test: + runs-on: ubuntu-latest + steps: + - run: echo helloworld +`), + }, + }, + Message: "add workflow", + OldBranch: "main", + NewBranch: "main", + Author: &files_service.IdentityOptions{ + GitUserName: user2.Name, + GitUserEmail: user2.Email, + }, + Committer: &files_service.IdentityOptions{ + GitUserName: user2.Name, + GitUserEmail: user2.Email, + }, + Dates: &files_service.CommitDateOptions{ + Author: time.Now(), + Committer: time.Now(), + }, + }) + require.NoError(t, err) + require.NotNil(t, addWorkflowToBaseResp) + + values := url.Values{} + values.Set("ref", "main") + req := NewRequestWithURLValues(t, "POST", fmt.Sprintf("/api/v1/repos/%s/actions/workflows/push-only.yml/dispatches", repo.FullName()), values). + AddTokenAuth(token) + resp := MakeRequest(t, req, http.StatusUnprocessableEntity) + apiError := DecodeJSON(t, resp, &api.APIError{}) + assert.Contains(t, apiError.Message, "has no workflow_dispatch event trigger") + + unittest.AssertNotExistsBean(t, &actions_model.ActionRun{ + RepoID: repo.ID, + Event: "workflow_dispatch", + WorkflowID: "push-only.yml", + }) + }) +} + func TestWorkflowDispatchPublicApiWithInputs(t *testing.T) { onGiteaRun(t, func(t *testing.T, u *url.URL) { user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})