初始提交: Gitea 项目代码
This commit is contained in:
@@ -0,0 +1,140 @@
|
||||
// Copyright 2026 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package actions
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"slices"
|
||||
"time"
|
||||
|
||||
"gitea.dev/models/db"
|
||||
user_model "gitea.dev/models/user"
|
||||
"gitea.dev/modules/log"
|
||||
"gitea.dev/modules/timeutil"
|
||||
"gitea.dev/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")
|
||||
}
|
||||
Reference in New Issue
Block a user