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

@@ -71,11 +71,11 @@ func init() {
db.RegisterModel(new(ActionRunIndex))
}
func (run *ActionRun) HTMLURL() string {
func (run *ActionRun) HTMLURL(ctxOpt ...context.Context) string {
if run.Repo == nil {
return ""
}
return fmt.Sprintf("%s/actions/runs/%d", run.Repo.HTMLURL(), run.ID)
return fmt.Sprintf("%s/actions/runs/%d", run.Repo.HTMLURL(ctxOpt...), run.ID)
}
func (run *ActionRun) Link() string {
@@ -120,11 +120,7 @@ func (run *ActionRun) RefTooltip() string {
}
// LoadAttributes load Repo TriggerUser if not loaded
func (run *ActionRun) LoadAttributes(ctx context.Context) (err error) {
if run == nil {
return nil
}
func (run *ActionRun) LoadAttributes(ctx context.Context) error {
if err := run.LoadRepo(ctx); err != nil {
return err
}
@@ -133,18 +129,19 @@ func (run *ActionRun) LoadAttributes(ctx context.Context) (err error) {
return err
}
if run.TriggerUser == nil {
run.TriggerUserID, run.TriggerUser, err = user_model.GetPossibleUserByID(ctx, run.TriggerUserID)
if err != nil {
return err
}
}
return run.LoadTriggerUser(ctx)
}
return nil
func (run *ActionRun) LoadTriggerUser(ctx context.Context) (err error) {
if run.TriggerUser != nil {
return nil
}
run.TriggerUserID, run.TriggerUser, err = user_model.GetPossibleUserByID(ctx, run.TriggerUserID)
return err
}
func (run *ActionRun) LoadRepo(ctx context.Context) error {
if run == nil || run.Repo != nil {
if run.Repo != nil {
return nil
}

View File

@@ -51,10 +51,6 @@ func (attempt *ActionRunAttempt) Duration() time.Duration {
}
func (attempt *ActionRunAttempt) LoadAttributes(ctx context.Context) (err error) {
if attempt == nil {
return nil
}
if attempt.Run == nil {
run, err := GetRunByRepoAndID(ctx, attempt.RepoID, attempt.RunID)
if err != nil {

View File

@@ -120,10 +120,6 @@ func (job *ActionRunJob) LoadRepo(ctx context.Context) error {
// LoadAttributes load Run if not loaded
func (job *ActionRunJob) LoadAttributes(ctx context.Context) error {
if job == nil {
return nil
}
if err := job.LoadRun(ctx); err != nil {
return err
}

View File

@@ -56,8 +56,10 @@ func (jobs ActionJobList) LoadRuns(ctx context.Context, withRepo bool) error {
return err
}
for _, j := range jobs {
if j.RunID > 0 && j.Run == nil {
if j.Run == nil {
j.Run = runs[j.RunID]
}
if j.Run != nil {
j.Run.Repo = j.Repo
}
}

View File

@@ -7,6 +7,7 @@ import (
"context"
"code.gitea.io/gitea/models/db"
repo_model "code.gitea.io/gitea/models/repo"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/container"
"code.gitea.io/gitea/modules/translation"
@@ -17,27 +18,39 @@ import (
type RunList []*ActionRun
// GetUserIDs returns a slice of user's id
func (runs RunList) GetUserIDs() []int64 {
return container.FilterSlice(runs, func(run *ActionRun) (int64, bool) {
return run.TriggerUserID, true
})
}
func (runs RunList) LoadTriggerUser(ctx context.Context) error {
userIDs := runs.GetUserIDs()
userIDs := container.FilterSlice(runs, func(run *ActionRun) (int64, bool) {
return run.TriggerUserID, run.TriggerUser == nil
})
users := make(map[int64]*user_model.User, len(userIDs))
if err := db.GetEngine(ctx).In("id", userIDs).Find(&users); err != nil {
return err
}
for _, run := range runs {
if run.TriggerUserID == user_model.ActionsUserID {
run.TriggerUser = user_model.NewActionsUser()
} else {
run.TriggerUser = users[run.TriggerUserID]
if run.TriggerUser == nil {
run.TriggerUser = user_model.NewGhostUser()
}
if run.TriggerUser != nil {
continue
}
run.TriggerUser = users[run.TriggerUserID]
if run.TriggerUserID < 0 {
run.TriggerUserID, run.TriggerUser, _ = user_model.GetPossibleUserByID(ctx, run.TriggerUserID)
} else if run.TriggerUser == nil {
run.TriggerUserID, run.TriggerUser, _ = user_model.GetPossibleUserByID(ctx, user_model.GhostUserID)
}
}
return nil
}
func (runs RunList) LoadRepos(ctx context.Context) error {
repoIDs := container.FilterSlice(runs, func(run *ActionRun) (int64, bool) {
return run.RepoID, run.Repo == nil
})
repos, err := repo_model.GetRepositoriesMapByIDs(ctx, repoIDs)
if err != nil {
return err
}
for _, run := range runs {
if run.Repo == nil {
run.Repo = repos[run.RepoID]
}
}
return nil

View File

@@ -125,9 +125,6 @@ func (task *ActionTask) LoadJob(ctx context.Context) error {
// LoadAttributes load Job Steps if not loaded
func (task *ActionTask) LoadAttributes(ctx context.Context) error {
if task == nil {
return nil
}
if err := task.LoadJob(ctx); err != nil {
return err
}

View File

@@ -376,8 +376,9 @@ func (repo *Repository) CommitLink(commitID string) (result string) {
}
// APIURL returns the repository API URL
func (repo *Repository) APIURL() string {
return setting.AppURL + "api/v1/repos/" + url.PathEscape(repo.OwnerName) + "/" + url.PathEscape(repo.Name)
func (repo *Repository) APIURL(ctxOpt ...context.Context) string {
ctx := util.OptionalArg(ctxOpt, context.TODO())
return httplib.MakeAbsoluteURL(ctx, setting.AppSubURL+"/api/v1/repos/"+url.PathEscape(repo.OwnerName)+"/"+url.PathEscape(repo.Name))
}
// GetCommitsCountCacheKey returns cache key used for commits count caching.