Modify the REST endpoint that triggers workflows to provide the data required to track the progress of those workflows.

This commit is contained in:
Mark Turney 2025-03-10 11:52:12 +04:00
commit 69dd7cb15d
7 changed files with 87 additions and 13 deletions

View file

@ -13,3 +13,12 @@ type DispatchWorkflowOption struct {
// Input keys and values configured in the workflow file. // Input keys and values configured in the workflow file.
Inputs map[string]string `json:"inputs"` Inputs map[string]string `json:"inputs"`
} }
// DispatchWorkflowRun represents a workflow run
// swagger:model
type DispatchWorkflowRun struct {
// the workflow run id
ID int64 `json:"id"`
// the jobs name
Jobs []string `json:"jobs"`
}

View file

@ -639,7 +639,11 @@ func DispatchWorkflow(ctx *context.APIContext) {
// in: body // in: body
// schema: // schema:
// "$ref": "#/definitions/DispatchWorkflowOption" // "$ref": "#/definitions/DispatchWorkflowOption"
// produces:
// - application/json
// responses: // responses:
// "201":
// "$ref": "#/responses/DispatchWorkflowRun"
// "204": // "204":
// "$ref": "#/responses/empty" // "$ref": "#/responses/empty"
// "404": // "404":
@ -670,7 +674,8 @@ func DispatchWorkflow(ctx *context.APIContext) {
return opt.Inputs[key] return opt.Inputs[key]
} }
if err := workflow.Dispatch(ctx, inputGetter, ctx.Repo.Repository, ctx.Doer); err != nil { run, jobs, err := workflow.Dispatch(ctx, inputGetter, ctx.Repo.Repository, ctx.Doer)
if err != nil {
if actions_service.IsInputRequiredErr(err) { if actions_service.IsInputRequiredErr(err) {
ctx.Error(http.StatusBadRequest, "workflow.Dispatch", err) ctx.Error(http.StatusBadRequest, "workflow.Dispatch", err)
} else { } else {
@ -679,5 +684,10 @@ func DispatchWorkflow(ctx *context.APIContext) {
return return
} }
ctx.JSON(http.StatusNoContent, nil) workflowRun := &api.DispatchWorkflowRun{
ID: run.ID,
Jobs: jobs,
}
ctx.JSON(http.StatusCreated, workflowRun)
} }

View file

@ -39,3 +39,10 @@ type swaggerRunJobList struct {
// in:body // in:body
Body []*api.ActionRunJob `json:"body"` Body []*api.ActionRunJob `json:"body"`
} }
// DispatchWorkflowRun is a Workflow Run after dispatching
// swagger:response DispatchWorkflowRun
type swaggerDispatchWorkflowRun struct {
// in:body
Body *api.DispatchWorkflowRun `json:"body"`
}

View file

@ -46,7 +46,8 @@ func ManualRunWorkflow(ctx *context_module.Context) {
return ctx.Req.PostFormValue(formKey) return ctx.Req.PostFormValue(formKey)
} }
if err := workflow.Dispatch(ctx, formKeyGetter, ctx.Repo.Repository, ctx.Doer); err != nil { _, _, err = workflow.Dispatch(ctx, formKeyGetter, ctx.Repo.Repository, ctx.Doer)
if err != nil {
if actions_service.IsInputRequiredErr(err) { if actions_service.IsInputRequiredErr(err) {
ctx.Flash.Error(ctx.Locale.Tr("actions.workflow.dispatch.input_required", err.(actions_service.InputRequiredErr).Name)) ctx.Flash.Error(ctx.Locale.Tr("actions.workflow.dispatch.input_required", err.(actions_service.InputRequiredErr).Name))
ctx.Redirect(location) ctx.Redirect(location)

View file

@ -49,15 +49,15 @@ type Workflow struct {
type InputValueGetter func(key string) string type InputValueGetter func(key string) string
func (entry *Workflow) Dispatch(ctx context.Context, inputGetter InputValueGetter, repo *repo_model.Repository, doer *user.User) error { func (entry *Workflow) Dispatch(ctx context.Context, inputGetter InputValueGetter, repo *repo_model.Repository, doer *user.User) (r *actions_model.ActionRun, j []string, err error) {
content, err := actions.GetContentFromEntry(entry.GitEntry) content, err := actions.GetContentFromEntry(entry.GitEntry)
if err != nil { if err != nil {
return err return nil, nil, err
} }
wf, err := act_model.ReadWorkflow(bytes.NewReader(content)) wf, err := act_model.ReadWorkflow(bytes.NewReader(content))
if err != nil { if err != nil {
return err return nil, nil, err
} }
fullWorkflowID := ".forgejo/workflows/" + entry.WorkflowID fullWorkflowID := ".forgejo/workflows/" + entry.WorkflowID
@ -79,7 +79,7 @@ func (entry *Workflow) Dispatch(ctx context.Context, inputGetter InputValueGette
if len(name) == 0 { if len(name) == 0 {
name = key name = key
} }
return InputRequiredErr{Name: name} return nil, nil, InputRequiredErr{Name: name}
} }
continue continue
} }
@ -92,7 +92,12 @@ func (entry *Workflow) Dispatch(ctx context.Context, inputGetter InputValueGette
} }
if int64(len(inputs)) > setting.Actions.LimitDispatchInputs { if int64(len(inputs)) > setting.Actions.LimitDispatchInputs {
return errors.New("to many inputs") return nil, nil, errors.New("to many inputs")
}
var jobNames []string
for jobName, _ := range wf.Jobs {
jobNames = append(jobNames, jobName)
} }
payload := &structs.WorkflowDispatchPayload{ payload := &structs.WorkflowDispatchPayload{
@ -105,7 +110,7 @@ func (entry *Workflow) Dispatch(ctx context.Context, inputGetter InputValueGette
p, err := json.Marshal(payload) p, err := json.Marshal(payload)
if err != nil { if err != nil {
return err return nil, nil, err
} }
run := &actions_model.ActionRun{ run := &actions_model.ActionRun{
@ -126,15 +131,15 @@ func (entry *Workflow) Dispatch(ctx context.Context, inputGetter InputValueGette
vars, err := actions_model.GetVariablesOfRun(ctx, run) vars, err := actions_model.GetVariablesOfRun(ctx, run)
if err != nil { if err != nil {
return err return nil, nil, err
} }
jobs, err := jobparser.Parse(content, jobparser.WithVars(vars)) jobs, err := jobparser.Parse(content, jobparser.WithVars(vars))
if err != nil { if err != nil {
return err return nil, nil, err
} }
return actions_model.InsertRun(ctx, run, jobs) return run, jobNames, actions_model.InsertRun(ctx, run, jobs)
} }
func GetWorkflowFromCommit(gitRepo *git.Repository, ref, workflowID string) (*Workflow, error) { func GetWorkflowFromCommit(gitRepo *git.Repository, ref, workflowID string) (*Workflow, error) {

View file

@ -5378,6 +5378,9 @@
"consumes": [ "consumes": [
"application/json" "application/json"
], ],
"produces": [
"application/json"
],
"tags": [ "tags": [
"repository" "repository"
], ],
@ -5414,6 +5417,9 @@
} }
], ],
"responses": { "responses": {
"201": {
"$ref": "#/responses/DispatchWorkflowRun"
},
"204": { "204": {
"$ref": "#/responses/empty" "$ref": "#/responses/empty"
}, },
@ -23132,6 +23138,27 @@
}, },
"x-go-package": "code.gitea.io/gitea/modules/structs" "x-go-package": "code.gitea.io/gitea/modules/structs"
}, },
"DispatchWorkflowRun": {
"description": "DispatchWorkflowRun represents a workflow run",
"type": "object",
"properties": {
"id": {
"description": "the workflow run id",
"type": "integer",
"format": "int64",
"x-go-name": "ID"
},
"jobs": {
"description": "the jobs name",
"type": "array",
"items": {
"type": "string"
},
"x-go-name": "Jobs"
}
},
"x-go-package": "code.gitea.io/gitea/modules/structs"
},
"EditAttachmentOptions": { "EditAttachmentOptions": {
"description": "EditAttachmentOptions options for editing attachments", "description": "EditAttachmentOptions options for editing attachments",
"type": "object", "type": "object",
@ -28511,6 +28538,12 @@
} }
} }
}, },
"DispatchWorkflowRun": {
"description": "DispatchWorkflowRun is a Workflow Run after dispatching",
"schema": {
"$ref": "#/definitions/DispatchWorkflowRun"
}
},
"EmailList": { "EmailList": {
"description": "EmailList", "description": "EmailList",
"schema": { "schema": {

View file

@ -752,9 +752,18 @@ func TestWorkflowDispatchEvent(t *testing.T) {
return "" return ""
} }
err = workflow.Dispatch(db.DefaultContext, inputGetter, repo, user2) var r *actions_model.ActionRun
var j []string
r, j, err = workflow.Dispatch(db.DefaultContext, inputGetter, repo, user2)
require.NoError(t, err) require.NoError(t, err)
assert.Equal(t, 1, unittest.GetCount(t, &actions_model.ActionRun{RepoID: repo.ID})) assert.Equal(t, 1, unittest.GetCount(t, &actions_model.ActionRun{RepoID: repo.ID}))
assert.Equal(t, "test", r.Title)
assert.Equal(t, "dispatch.yml", r.WorkflowID)
assert.Equal(t, sha, r.CommitSHA)
assert.Equal(t, actions_module.GithubEventWorkflowDispatch, r.TriggerEvent)
assert.Len(t, j, 1)
assert.Equal(t, "test", j[0])
}) })
} }