mirror of
https://github.com/go-gitea/gitea.git
synced 2026-05-04 14:52:52 +00:00
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>
141 lines
4.2 KiB
Go
141 lines
4.2 KiB
Go
// Copyright 2026 The Gitea Authors. All rights reserved.
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
package actions
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"slices"
|
|
"time"
|
|
|
|
"code.gitea.io/gitea/models/db"
|
|
user_model "code.gitea.io/gitea/models/user"
|
|
"code.gitea.io/gitea/modules/log"
|
|
"code.gitea.io/gitea/modules/timeutil"
|
|
"code.gitea.io/gitea/modules/util"
|
|
)
|
|
|
|
// ActionRunAttempt represents a single execution attempt of an ActionRun.
|
|
type ActionRunAttempt struct {
|
|
ID int64
|
|
RepoID int64 `xorm:"index(repo_concurrency_status)"`
|
|
RunID int64 `xorm:"UNIQUE(run_attempt)"`
|
|
Run *ActionRun `xorm:"-"`
|
|
Attempt int64 `xorm:"UNIQUE(run_attempt)"`
|
|
|
|
TriggerUserID int64
|
|
TriggerUser *user_model.User `xorm:"-"`
|
|
|
|
ConcurrencyGroup string `xorm:"index(repo_concurrency_status) NOT NULL DEFAULT ''"`
|
|
ConcurrencyCancel bool `xorm:"NOT NULL DEFAULT FALSE"`
|
|
|
|
Status Status `xorm:"index(repo_concurrency_status)"`
|
|
Started timeutil.TimeStamp
|
|
Stopped timeutil.TimeStamp
|
|
|
|
Created timeutil.TimeStamp `xorm:"created"`
|
|
Updated timeutil.TimeStamp `xorm:"updated"`
|
|
}
|
|
|
|
func (*ActionRunAttempt) TableName() string {
|
|
return "action_run_attempt"
|
|
}
|
|
|
|
func init() {
|
|
db.RegisterModel(new(ActionRunAttempt))
|
|
}
|
|
|
|
func (attempt *ActionRunAttempt) Duration() time.Duration {
|
|
return calculateDuration(attempt.Started, attempt.Stopped, attempt.Status, attempt.Updated)
|
|
}
|
|
|
|
func (attempt *ActionRunAttempt) LoadAttributes(ctx context.Context) (err error) {
|
|
if attempt.Run == nil {
|
|
run, err := GetRunByRepoAndID(ctx, attempt.RepoID, attempt.RunID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if err := run.LoadAttributes(ctx); err != nil {
|
|
return err
|
|
}
|
|
attempt.Run = run
|
|
}
|
|
|
|
if attempt.TriggerUser == nil {
|
|
attempt.TriggerUserID, attempt.TriggerUser, err = user_model.GetPossibleUserByID(ctx, attempt.TriggerUserID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func GetRunAttemptByRepoAndID(ctx context.Context, repoID, attemptID int64) (*ActionRunAttempt, error) {
|
|
var attempt ActionRunAttempt
|
|
has, err := db.GetEngine(ctx).Where("repo_id=? AND id=?", repoID, attemptID).Get(&attempt)
|
|
if err != nil {
|
|
return nil, err
|
|
} else if !has {
|
|
return nil, fmt.Errorf("run attempt %d in repo %d: %w", attemptID, repoID, util.ErrNotExist)
|
|
}
|
|
return &attempt, nil
|
|
}
|
|
|
|
func GetRunAttemptByRunIDAndAttemptNum(ctx context.Context, runID, attemptNum int64) (*ActionRunAttempt, error) {
|
|
var attempt ActionRunAttempt
|
|
has, err := db.GetEngine(ctx).Where("run_id=? AND attempt=?", runID, attemptNum).Get(&attempt)
|
|
if err != nil {
|
|
return nil, err
|
|
} else if !has {
|
|
return nil, fmt.Errorf("run attempt %d for run %d: %w", attemptNum, runID, util.ErrNotExist)
|
|
}
|
|
return &attempt, nil
|
|
}
|
|
|
|
// FindConcurrentRunAttempts returns attempts in the given concurrency group and status set.
|
|
// Results are unordered; callers must not depend on any particular row order.
|
|
func FindConcurrentRunAttempts(ctx context.Context, repoID int64, concurrencyGroup string, statuses []Status) ([]*ActionRunAttempt, error) {
|
|
attempts := make([]*ActionRunAttempt, 0)
|
|
sess := db.GetEngine(ctx).Where("repo_id=? AND concurrency_group=?", repoID, concurrencyGroup)
|
|
if len(statuses) > 0 {
|
|
sess = sess.In("status", statuses)
|
|
}
|
|
return attempts, sess.Find(&attempts)
|
|
}
|
|
|
|
func UpdateRunAttempt(ctx context.Context, attempt *ActionRunAttempt, cols ...string) error {
|
|
if slices.Contains(cols, "status") && attempt.Started.IsZero() && attempt.Status.IsRunning() {
|
|
attempt.Started = timeutil.TimeStampNow()
|
|
cols = append(cols, "started")
|
|
}
|
|
|
|
sess := db.GetEngine(ctx).ID(attempt.ID)
|
|
if len(cols) > 0 {
|
|
sess.Cols(cols...)
|
|
}
|
|
if _, err := sess.Update(attempt); err != nil {
|
|
return err
|
|
}
|
|
|
|
// Only status/timing changes on an attempt need to update the latest run.
|
|
if len(cols) > 0 && !slices.Contains(cols, "status") && !slices.Contains(cols, "started") && !slices.Contains(cols, "stopped") {
|
|
return nil
|
|
}
|
|
|
|
run, err := GetRunByRepoAndID(ctx, attempt.RepoID, attempt.RunID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if run.LatestAttemptID != attempt.ID {
|
|
log.Warn("run %d cannot be updated by an old attempt %d", run.LatestAttemptID, attempt.ID)
|
|
return nil
|
|
}
|
|
|
|
run.Status = attempt.Status
|
|
run.Started = attempt.Started
|
|
run.Stopped = attempt.Stopped
|
|
return UpdateRun(ctx, run, "status", "started", "stopped")
|
|
}
|