Batch-load related data in actions run, job, and task API endpoints (#37032)

Avoid per-item DB queries in ListRuns, ListJobs, and ListActionTasks by
batch-loading trigger users, repositories, and task attributes before
the conversion loop. Remove ReferencesGitRepo from the /actions route
group since no task/run endpoints use it.

Added tests for these endpoints as well.

---------

Signed-off-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: silverwind <me@silverwind.io>
Co-authored-by: Claude (Opus 4.7) <noreply@anthropic.com>
This commit is contained in:
Myers Carpenter
2026-04-29 04:39:43 -04:00
committed by GitHub
parent 0ba862cb97
commit 18762c7748
15 changed files with 214 additions and 135 deletions

View File

@@ -815,7 +815,8 @@ func (n *actionsNotifier) WorkflowRunStatusUpdate(ctx context.Context, repo *rep
log.Error("GetActionWorkflow: %v", err)
return
}
convertedRun, err := convert.ToActionWorkflowRun(ctx, repo, run, nil)
run.Repo = repo
convertedRun, err := convert.ToActionWorkflowRun(ctx, run, nil)
if err != nil {
log.Error("ToActionWorkflowRun: %v", err)
return

View File

@@ -115,12 +115,12 @@ func TestToActionWorkflowRun_UsesTriggerEvent(t *testing.T) {
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2})
run := unittest.AssertExistsAndLoadBean(t, &actions_model.ActionRun{ID: 803})
run.Repo = repo
// Scheduled runs keep Event as the registration event (push) and use TriggerEvent as the real trigger.
run.Event = "push"
run.TriggerEvent = "schedule"
apiRun, err := ToActionWorkflowRun(t.Context(), repo, run, nil)
apiRun, err := ToActionWorkflowRun(t.Context(), run, nil)
require.NoError(t, err)
assert.Equal(t, "schedule", apiRun.Event)
}

View File

@@ -29,6 +29,7 @@ import (
"code.gitea.io/gitea/modules/actions"
"code.gitea.io/gitea/modules/container"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/httplib"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
api "code.gitea.io/gitea/modules/structs"
@@ -222,14 +223,18 @@ func ToTag(repo *repo_model.Repository, t *git.Tag) *api.Tag {
}
}
// ToActionTask convert a actions_model.ActionTask to an api.ActionTask
// ToActionTask convert an actions_model.ActionTask to an api.ActionTask
func ToActionTask(ctx context.Context, t *actions_model.ActionTask) (*api.ActionTask, error) {
if err := t.LoadAttributes(ctx); err != nil {
// don't need Steps here, only need to load job and its run
if err := t.LoadJob(ctx); err != nil {
return nil, err
}
if err := t.Job.LoadRun(ctx); err != nil {
return nil, err
}
if err := t.Job.Run.LoadRepo(ctx); err != nil {
return nil, err
}
url := strings.TrimSuffix(setting.AppURL, "/") + t.GetRunLink()
return &api.ActionTask{
ID: t.ID,
Name: t.Job.Name,
@@ -240,23 +245,25 @@ func ToActionTask(ctx context.Context, t *actions_model.ActionTask) (*api.Action
DisplayTitle: t.Job.Run.Title,
Status: t.Status.String(),
WorkflowID: t.Job.Run.WorkflowID,
URL: url,
URL: httplib.MakeAbsoluteURL(ctx, t.Job.Run.Link()),
CreatedAt: t.Created.AsLocalTime(),
UpdatedAt: t.Updated.AsLocalTime(),
RunStartedAt: t.Started.AsLocalTime(),
}, nil
}
func ToActionWorkflowRun(ctx context.Context, repo *repo_model.Repository, run *actions_model.ActionRun, attempt *actions_model.ActionRunAttempt) (*api.ActionWorkflowRun, error) {
if err := run.LoadAttributes(ctx); err != nil {
func ToActionWorkflowRun(ctx context.Context, run *actions_model.ActionRun, attempt *actions_model.ActionRunAttempt) (_ *api.ActionWorkflowRun, err error) {
if err := run.LoadRepo(ctx); err != nil {
return nil, err
}
if err := run.LoadTriggerUser(ctx); err != nil {
return nil, err
}
if attempt == nil {
if latestAttempt, has, err := run.GetLatestAttempt(ctx); err != nil {
attempt, _, err = run.GetLatestAttempt(ctx)
if err != nil {
return nil, err
} else if has {
attempt = latestAttempt
}
}
@@ -272,6 +279,7 @@ func ToActionWorkflowRun(ctx context.Context, repo *repo_model.Repository, run *
var previousAttemptURL *string
if attempt != nil {
attempt.Run = run
if err := attempt.LoadAttributes(ctx); err != nil {
return nil, err
}
@@ -281,16 +289,15 @@ func ToActionWorkflowRun(ctx context.Context, repo *repo_model.Repository, run *
completedAt = attempt.Stopped.AsLocalTime()
triggerUser = attempt.TriggerUser
if attempt.Attempt > 1 {
url := fmt.Sprintf("%s/actions/runs/%d/attempts/%d", repo.APIURL(), run.ID, attempt.Attempt-1)
previousAttemptURL = &url
previousAttemptURL = new(fmt.Sprintf("%s/actions/runs/%d/attempts/%d", run.Repo.APIURL(ctx), run.ID, attempt.Attempt-1))
}
}
return &api.ActionWorkflowRun{
ID: run.ID,
URL: fmt.Sprintf("%s/actions/runs/%d", repo.APIURL(), run.ID),
URL: fmt.Sprintf("%s/actions/runs/%d", run.Repo.APIURL(ctx), run.ID),
PreviousAttemptURL: previousAttemptURL,
HTMLURL: run.HTMLURL(),
HTMLURL: run.HTMLURL(ctx),
RunNumber: run.Index,
RunAttempt: runAttempt,
StartedAt: startedAt,
@@ -302,7 +309,7 @@ func ToActionWorkflowRun(ctx context.Context, repo *repo_model.Repository, run *
Status: status,
Conclusion: conclusion,
Path: fmt.Sprintf("%s@%s", run.WorkflowID, run.Ref),
Repository: ToRepo(ctx, repo, access_model.Permission{AccessMode: perm.AccessModeNone}),
Repository: ToRepo(ctx, run.Repo, access_model.Permission{AccessMode: perm.AccessModeNone}),
TriggerActor: ToUser(ctx, triggerUser, nil),
Actor: ToUser(ctx, actor, nil),
}, nil
@@ -400,11 +407,11 @@ func ToActionWorkflowJob(ctx context.Context, repo *repo_model.Repository, task
return &api.ActionWorkflowJob{
ID: job.ID,
// missing api endpoint for this location
URL: fmt.Sprintf("%s/actions/jobs/%d", repo.APIURL(), job.ID),
HTMLURL: fmt.Sprintf("%s/jobs/%d", job.Run.HTMLURL(), job.ID),
URL: fmt.Sprintf("%s/actions/jobs/%d", repo.APIURL(ctx), job.ID),
HTMLURL: fmt.Sprintf("%s/jobs/%d", job.Run.HTMLURL(ctx), job.ID),
RunID: job.RunID,
// Missing api endpoint for this location, artifacts are available under a nested url
RunURL: fmt.Sprintf("%s/actions/runs/%d", repo.APIURL(), job.RunID),
RunURL: fmt.Sprintf("%s/actions/runs/%d", repo.APIURL(ctx), job.RunID),
Name: job.Name,
Labels: job.RunsOn,
RunAttempt: job.Attempt,

View File

@@ -1043,7 +1043,8 @@ func (*webhookNotifier) WorkflowRunStatusUpdate(ctx context.Context, repo *repo_
return
}
convertedRun, err := convert.ToActionWorkflowRun(ctx, repo, run, nil)
run.Repo = repo
convertedRun, err := convert.ToActionWorkflowRun(ctx, run, nil)
if err != nil {
log.Error("ToActionWorkflowRun: %v", err)
return