diff --git a/modules/structs/action.go b/modules/structs/action.go index 2c42365c19..f47b228d75 100644 --- a/modules/structs/action.go +++ b/modules/structs/action.go @@ -78,3 +78,9 @@ type ActionRun struct { // the url of this action run HTMLURL string `json:"html_url"` } + +// ListActionRunResponse return a list of ActionRun +type ListActionRunResponse struct { + Entries []*ActionRun `json:"workflow_runs"` + TotalCount int64 `json:"total_count"` +} diff --git a/modules/structs/repo_actions.go b/modules/structs/repo_actions.go index 505367336c..b13f344738 100644 --- a/modules/structs/repo_actions.go +++ b/modules/structs/repo_actions.go @@ -32,23 +32,3 @@ type ActionTaskResponse struct { Entries []*ActionTask `json:"workflow_runs"` TotalCount int64 `json:"total_count"` } - -// ActionRun represents an ActionRun -type RepoActionRun struct { - ID int64 `json:"id"` - Name string `json:"name"` - RunNumber int64 `json:"run_number"` - Event string `json:"event"` - Status string `json:"status"` - HeadBranch string `json:"head_branch"` - HeadSHA string `json:"head_sha"` - WorkflowID string `json:"workflow_id"` - URL string `json:"url"` - TriggeringActor *User `json:"triggering_actor"` -} - -// ListActionRunResponse return a list of ActionRun -type ListRepoActionRunResponse struct { - Entries []*RepoActionRun `json:"workflow_runs"` - TotalCount int64 `json:"total_count"` -} diff --git a/routers/api/v1/repo/action.go b/routers/api/v1/repo/action.go index 03089a18d3..dbc4933de6 100644 --- a/routers/api/v1/repo/action.go +++ b/routers/api/v1/repo/action.go @@ -748,7 +748,7 @@ func ListActionRuns(ctx *context.APIContext) { // type: string // responses: // "200": - // "$ref": "#/responses/RepoActionRunList" + // "$ref": "#/responses/ActionRunList" // "400": // "$ref": "#/responses/error" // "403": @@ -779,16 +779,16 @@ func ListActionRuns(ctx *context.APIContext) { return } - res := new(api.ListRepoActionRunResponse) + res := new(api.ListActionRunResponse) res.TotalCount = total - res.Entries = make([]*api.RepoActionRun, len(runs)) + res.Entries = make([]*api.ActionRun, len(runs)) for i, r := range runs { - cr, err := convert.ToRepoActionRun(ctx, r) - if err != nil { - ctx.Error(http.StatusInternalServerError, "ToActionRun", err) + if err := r.LoadAttributes(ctx); err != nil { + ctx.Error(http.StatusInternalServerError, "LoadAttributes", err) return } + cr := convert.ToActionRun(ctx, r, ctx.Doer) res.Entries[i] = cr } @@ -821,7 +821,7 @@ func GetActionRun(ctx *context.APIContext) { // required: true // responses: // "200": - // "$ref": "#/responses/RepoActionRun" + // "$ref": "#/responses/ActionRun" // "400": // "$ref": "#/responses/error" // "403": @@ -839,16 +839,17 @@ func GetActionRun(ctx *context.APIContext) { return } + // Action runs lives in its own table, therefore we check that the + // run with the requested ID is owned by the repository if ctx.Repo.Repository.ID != run.RepoID { ctx.Error(http.StatusNotFound, "GetRunById", util.ErrNotExist) return } - res, err := convert.ToRepoActionRun(ctx, run) - if err != nil { - ctx.Error(http.StatusInternalServerError, "ToRepoActionRun", err) + if err := run.LoadAttributes(ctx); err != nil { + ctx.Error(http.StatusInternalServerError, "LoadAttributes", err) return } - ctx.JSON(http.StatusOK, res) + ctx.JSON(http.StatusOK, convert.ToActionRun(ctx, run, ctx.Doer)) } diff --git a/routers/api/v1/swagger/repo.go b/routers/api/v1/swagger/repo.go index bde0efea4e..cd4832e15f 100644 --- a/routers/api/v1/swagger/repo.go +++ b/routers/api/v1/swagger/repo.go @@ -463,16 +463,16 @@ type swaggerSyncForkInfo struct { Body []api.SyncForkInfo `json:"body"` } -// RepoActionRunList -// swagger:response RepoActionRunList -type swaggerRepoActionRunList struct { +// ActionRunList +// swagger:response ActionRunList +type swaggerActionRunList struct { // in:body - Body api.ListRepoActionRunResponse `json:"body"` + Body api.ListActionRunResponse `json:"body"` } -// RepoActionRun -// swagger:response RepoActionRun -type swaggerRepoActionRun struct { +// ActionRun +// swagger:response ActionRun +type swaggerActionRun struct { // in:body - Body api.RepoActionRun `json:"body"` + Body api.ActionRun `json:"body"` } diff --git a/services/convert/action.go b/services/convert/action.go index 5e17172b45..703c1f1261 100644 --- a/services/convert/action.go +++ b/services/convert/action.go @@ -8,22 +8,17 @@ import ( actions_model "forgejo.org/models/actions" access_model "forgejo.org/models/perm/access" + user_model "forgejo.org/models/user" api "forgejo.org/modules/structs" ) // ToActionRun convert actions_model.User to api.ActionRun // the run needs all attributes loaded -func ToActionRun(ctx context.Context, run *actions_model.ActionRun) *api.ActionRun { +func ToActionRun(ctx context.Context, run *actions_model.ActionRun, doer *user_model.User) *api.ActionRun { if run == nil { return nil } - // The doer is the one whose perspective is used to view this ActionRun. - // In the best case we use the user that created the webhook. - // Unfortunately we don't know who that was. - // So instead we use the repo owner, who is able to create webhooks and allow others to do so by making them repo admins. - // This is pretty close to perfect. - doer := run.Repo.Owner permissionInRepo, _ := access_model.GetUserRepoPermission(ctx, run.Repo, doer) return &api.ActionRun{ diff --git a/services/convert/convert.go b/services/convert/convert.go index 48da9d7623..2ea24a1b51 100644 --- a/services/convert/convert.go +++ b/services/convert/convert.go @@ -222,29 +222,6 @@ func ToActionTask(ctx context.Context, t *actions_model.ActionTask) (*api.Action }, nil } -// ToRepoActionRun convert a actions_model.ActionRun to an api.RepoActionRun -func ToRepoActionRun(ctx context.Context, r *actions_model.ActionRun) (*api.RepoActionRun, error) { - if err := r.LoadAttributes(ctx); err != nil { - return nil, err - } - - url := strings.TrimSuffix(setting.AppURL, "/") + r.Link() - actor := ToUser(ctx, r.TriggerUser, nil) - - return &api.RepoActionRun{ - ID: r.ID, - Name: r.Title, - HeadBranch: r.PrettyRef(), - HeadSHA: r.CommitSHA, - RunNumber: r.Index, - Event: r.TriggerEvent, - Status: r.Status.String(), - WorkflowID: r.WorkflowID, - URL: url, - TriggeringActor: actor, - }, nil -} - // ToVerification convert a git.Commit.Signature to an api.PayloadCommitVerification func ToVerification(ctx context.Context, c *git.Commit) *api.PayloadCommitVerification { verif := asymkey_model.ParseCommitWithSignature(ctx, c) diff --git a/services/webhook/notifier.go b/services/webhook/notifier.go index b3201e5d10..009efc994f 100644 --- a/services/webhook/notifier.go +++ b/services/webhook/notifier.go @@ -894,9 +894,16 @@ func (m *webhookNotifier) ActionRunNowDone(ctx context.Context, run *actions_mod Owner: run.TriggerUser, } + // The doer is the one whose perspective is used to view this ActionRun. + // In the best case we use the user that created the webhook. + // Unfortunately we don't know who that was. + // So instead we use the repo owner, who is able to create webhooks and allow others to do so by making them repo admins. + // This is pretty close to perfect. + doer := run.Repo.Owner + payload := &api.ActionPayload{ - Run: convert.ToActionRun(ctx, run), - LastRun: convert.ToActionRun(ctx, lastRun), + Run: convert.ToActionRun(ctx, run, doer), + LastRun: convert.ToActionRun(ctx, lastRun, doer), PriorStatus: priorStatus.String(), } diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl index bba4ac4949..3ef712c464 100644 --- a/templates/swagger/v1_json.tmpl +++ b/templates/swagger/v1_json.tmpl @@ -4985,7 +4985,7 @@ ], "responses": { "200": { - "$ref": "#/responses/RepoActionRunList" + "$ref": "#/responses/ActionRunList" }, "400": { "$ref": "#/responses/error" @@ -5032,7 +5032,7 @@ ], "responses": { "200": { - "$ref": "#/responses/RepoActionRun" + "$ref": "#/responses/ActionRun" }, "400": { "$ref": "#/responses/error" @@ -21120,6 +21120,129 @@ }, "x-go-package": "forgejo.org/modules/structs" }, + "ActionRun": { + "description": "ActionRun represents an action run", + "type": "object", + "properties": { + "ScheduleID": { + "description": "the cron id for the schedule trigger", + "type": "integer", + "format": "int64" + }, + "approved_by": { + "description": "who approved this action run", + "type": "integer", + "format": "int64", + "x-go-name": "ApprovedBy" + }, + "commit_sha": { + "description": "the commit sha the action run ran on", + "type": "string", + "x-go-name": "CommitSHA" + }, + "created": { + "description": "when the action run was created", + "type": "string", + "format": "date-time", + "x-go-name": "Created" + }, + "duration": { + "$ref": "#/definitions/Duration" + }, + "event": { + "description": "the webhook event that causes the workflow to run", + "type": "string", + "x-go-name": "Event" + }, + "event_payload": { + "description": "the payload of the webhook event that causes the workflow to run", + "type": "string", + "x-go-name": "EventPayload" + }, + "html_url": { + "description": "the url of this action run", + "type": "string", + "x-go-name": "HTMLURL" + }, + "id": { + "description": "the action run id", + "type": "integer", + "format": "int64", + "x-go-name": "ID" + }, + "index_in_repo": { + "description": "a unique number for each run of a repository", + "type": "integer", + "format": "int64", + "x-go-name": "Index" + }, + "is_fork_pull_request": { + "description": "If this is triggered by a PR from a forked repository or an untrusted user, we need to check if it is approved and limit permissions when running the workflow.", + "type": "boolean", + "x-go-name": "IsForkPullRequest" + }, + "is_ref_deleted": { + "description": "has the commit/tag/… the action run ran on been deleted", + "type": "boolean", + "x-go-name": "IsRefDeleted" + }, + "need_approval": { + "description": "may need approval if it's a fork pull request", + "type": "boolean", + "x-go-name": "NeedApproval" + }, + "prettyref": { + "description": "the commit/tag/… the action run ran on", + "type": "string", + "x-go-name": "PrettyRef" + }, + "repository": { + "$ref": "#/definitions/Repository" + }, + "started": { + "description": "when the action run was started", + "type": "string", + "format": "date-time", + "x-go-name": "Started" + }, + "status": { + "description": "the current status of this run", + "type": "string", + "x-go-name": "Status" + }, + "stopped": { + "description": "when the action run was stopped", + "type": "string", + "format": "date-time", + "x-go-name": "Stopped" + }, + "title": { + "description": "the action run's title", + "type": "string", + "x-go-name": "Title" + }, + "trigger_event": { + "description": "the trigger event defined in the `on` configuration of the triggered workflow", + "type": "string", + "x-go-name": "TriggerEvent" + }, + "trigger_user": { + "$ref": "#/definitions/User" + }, + "updated": { + "description": "when the action run was last updated", + "type": "string", + "format": "date-time", + "x-go-name": "Updated" + }, + "workflow_id": { + "description": "the name of workflow file", + "type": "string", + "x-go-name": "WorkflowID" + } + }, + "x-go-package": "forgejo.org/modules/structs" + }, "ActionRunJob": { "description": "ActionRunJob represents a job of a run", "type": "object", @@ -23610,6 +23733,12 @@ }, "x-go-package": "forgejo.org/modules/structs" }, + "Duration": { + "description": "A Duration represents the elapsed time between two instants\nas an int64 nanosecond count. The representation limits the\nlargest representable duration to approximately 290 years.", + "type": "integer", + "format": "int64", + "x-go-package": "time" + }, "EditAttachmentOptions": { "description": "EditAttachmentOptions options for editing attachments", "type": "object", @@ -25576,7 +25705,7 @@ }, "x-go-package": "forgejo.org/modules/structs" }, - "ListRepoActionRunResponse": { + "ListActionRunResponse": { "description": "ListActionRunResponse return a list of ActionRun", "type": "object", "properties": { @@ -25588,7 +25717,7 @@ "workflow_runs": { "type": "array", "items": { - "$ref": "#/definitions/RepoActionRun" + "$ref": "#/definitions/ActionRun" }, "x-go-name": "Entries" } @@ -27353,54 +27482,6 @@ }, "x-go-package": "forgejo.org/modules/structs" }, - "RepoActionRun": { - "description": "ActionRun represents an ActionRun", - "type": "object", - "properties": { - "event": { - "type": "string", - "x-go-name": "Event" - }, - "head_branch": { - "type": "string", - "x-go-name": "HeadBranch" - }, - "head_sha": { - "type": "string", - "x-go-name": "HeadSHA" - }, - "id": { - "type": "integer", - "format": "int64", - "x-go-name": "ID" - }, - "name": { - "type": "string", - "x-go-name": "Name" - }, - "run_number": { - "type": "integer", - "format": "int64", - "x-go-name": "RunNumber" - }, - "status": { - "type": "string", - "x-go-name": "Status" - }, - "triggering_actor": { - "$ref": "#/definitions/User" - }, - "url": { - "type": "string", - "x-go-name": "URL" - }, - "workflow_id": { - "type": "string", - "x-go-name": "WorkflowID" - } - }, - "x-go-package": "forgejo.org/modules/structs" - }, "RepoCollaboratorPermission": { "description": "RepoCollaboratorPermission to get repository permission for a collaborator", "type": "object", @@ -28847,6 +28928,18 @@ } } }, + "ActionRun": { + "description": "ActionRun", + "schema": { + "$ref": "#/definitions/ActionRun" + } + }, + "ActionRunList": { + "description": "ActionRunList", + "schema": { + "$ref": "#/definitions/ListActionRunResponse" + } + }, "ActionVariable": { "description": "ActionVariable", "schema": { @@ -29616,18 +29709,6 @@ } } }, - "RepoActionRun": { - "description": "RepoActionRun", - "schema": { - "$ref": "#/definitions/RepoActionRun" - } - }, - "RepoActionRunList": { - "description": "RepoActionRunList", - "schema": { - "$ref": "#/definitions/ListRepoActionRunResponse" - } - }, "RepoCollaboratorPermission": { "description": "RepoCollaboratorPermission", "schema": { diff --git a/tests/integration/api_repo_actions_test.go b/tests/integration/api_repo_actions_test.go index 34d603487c..af2aa10cfd 100644 --- a/tests/integration/api_repo_actions_test.go +++ b/tests/integration/api_repo_actions_test.go @@ -169,7 +169,7 @@ func TestAPIGetListActionRun(t *testing.T) { req.AddTokenAuth(token) res := MakeRequest(t, req, http.StatusOK) - apiRuns := new(api.ListRepoActionRunResponse) + apiRuns := new(api.ListActionRunResponse) DecodeJSON(t, res, apiRuns) assert.Equal(t, int64(len(tt.expectedIDs)), apiRuns.TotalCount) @@ -231,13 +231,13 @@ func TestAPIGetActionRun(t *testing.T) { } dbRun := unittest.AssertExistsAndLoadBean(t, &actions_model.ActionRun{ID: tt.runID}) - apiRun := new(api.RepoActionRun) + apiRun := new(api.ActionRun) DecodeJSON(t, res, apiRun) - assert.Equal(t, dbRun.Index, apiRun.RunNumber) + assert.Equal(t, dbRun.Index, apiRun.Index) assert.Equal(t, dbRun.Status.String(), apiRun.Status) - assert.Equal(t, dbRun.CommitSHA, apiRun.HeadSHA) - assert.Equal(t, dbRun.TriggerUserID, apiRun.TriggeringActor.ID) + assert.Equal(t, dbRun.CommitSHA, apiRun.CommitSHA) + assert.Equal(t, dbRun.TriggerUserID, apiRun.TriggerUser.ID) }) } }