fix(actions): use gitea's clock for actions durations (#39323)

This commit is contained in:
silverwind
2026-09-16 06:53:15 +00:00
committed by GitHub
parent 62945266d4
commit 7efd24b28f
7 changed files with 36 additions and 34 deletions
+4 -1
View File
@@ -174,7 +174,10 @@ func (run *ActionRun) LoadRepo(ctx context.Context) error {
}
func (run *ActionRun) Duration() time.Duration {
d := calculateDuration(run.Started, run.Stopped, run.Status, run.Updated) + run.PreviousDuration
d := calculateDuration(run.Started, run.Stopped, run.Status, run.Updated)
if run.LatestAttemptID == 0 {
d += run.PreviousDuration
}
if d < 0 {
return 0
}
+4
View File
@@ -163,6 +163,10 @@ func UpdateRunAttempt(ctx context.Context, attempt *ActionRunAttempt, cols ...st
attempt.Started = timeutil.TimeStampNow()
cols = append(cols, "started")
}
if slices.Contains(cols, "status") && !attempt.Stopped.IsZero() && !attempt.Status.IsDone() {
attempt.Stopped = 0
cols = append(cols, "stopped")
}
sess := db.GetEngine(ctx).ID(attempt.ID)
if len(cols) > 0 {
+7 -16
View File
@@ -21,7 +21,6 @@ import (
"gitea.dev/modules/timeutil"
"gitea.dev/modules/util"
"google.golang.org/protobuf/types/known/timestamppb"
"xorm.io/builder"
)
@@ -68,10 +67,6 @@ func init() {
db.RegisterModel(new(ActionTask))
}
func (task *ActionTask) Duration() time.Duration {
return calculateDuration(task.Started, task.Stopped, task.Status, task.Updated)
}
func (task *ActionTask) IsStopped() bool {
return task.Stopped > 0
}
@@ -484,6 +479,7 @@ func UpdateTaskByState(ctx context.Context, runnerID int64, state *runnerv1.Task
return nil
}
now := timeutil.TimeStampNow()
// state.Result is not unspecified means the task is finished
if state.Result != runnerv1.Result_RESULT_UNSPECIFIED {
if task.Status == StatusCancelling {
@@ -492,7 +488,7 @@ func UpdateTaskByState(ctx context.Context, runnerID int64, state *runnerv1.Task
} else {
task.Status = StatusFromResult(state.Result)
}
task.Stopped = timeutil.TimeStamp(state.StoppedAt.AsTime().Unix())
task.Stopped = now
if err := UpdateTask(ctx, task, "status", "stopped"); err != nil {
return err
}
@@ -506,7 +502,7 @@ func UpdateTaskByState(ctx context.Context, runnerID int64, state *runnerv1.Task
}
} else {
// Force update ActionTask.Updated to avoid the task being judged as a zombie task
task.Updated = timeutil.TimeStampNow()
task.Updated = now
if err := UpdateTask(ctx, task, "updated"); err != nil {
return err
}
@@ -522,11 +518,13 @@ func UpdateTaskByState(ctx context.Context, runnerID int64, state *runnerv1.Task
result = v.Result
step.LogIndex = v.LogIndex
step.LogLength = v.LogLength
step.Started = convertTimestamp(v.StartedAt)
step.Stopped = convertTimestamp(v.StoppedAt)
if step.Started == 0 && v.StartedAt != nil {
step.Started = now
}
}
if result != runnerv1.Result_RESULT_UNSPECIFIED {
step.Status = StatusFromResult(result)
step.Stopped = util.IfZero(step.Stopped, now)
} else if step.Started != 0 {
step.Status = StatusRunning
}
@@ -638,13 +636,6 @@ func FindOldTasksToExpire(ctx context.Context, olderThan timeutil.TimeStamp, lim
Find(&tasks)
}
func convertTimestamp(timestamp *timestamppb.Timestamp) timeutil.TimeStamp {
if timestamp.GetSeconds() == 0 && timestamp.GetNanos() == 0 {
return timeutil.TimeStamp(0)
}
return timeutil.TimeStamp(timestamp.AsTime().Unix())
}
func logFileName(repoFullName string, taskID int64) string {
ret := fmt.Sprintf("%s/%02x/%d.log", repoFullName, taskID%256, taskID)
+7 -2
View File
@@ -380,14 +380,19 @@ func TestUpdateTaskByStateIsAtomic(t *testing.T) {
task, job := newRunningTaskForCancelling(t, "atomic-report-job", true)
require.NoError(t, db.Insert(t.Context(), &ActionTaskStep{TaskID: task.ID, RepoID: task.RepoID, Status: StatusRunning}))
unittest.GetXORMEngine().AddHook(&failFirstStepWrite{})
finalState := &runnerv1.TaskState{Id: task.ID, Result: runnerv1.Result_RESULT_SUCCESS, StoppedAt: timestamppb.Now()}
skewed := &timestamppb.Timestamp{Seconds: 1}
finalState := &runnerv1.TaskState{Id: task.ID, Result: runnerv1.Result_RESULT_SUCCESS, StoppedAt: skewed, Steps: []*runnerv1.StepState{{StartedAt: skewed}}}
before := timeutil.TimeStampNow()
_, err := UpdateTaskByState(t.Context(), task.RunnerID, finalState)
require.Error(t, err)
assert.Equal(t, StatusRunning, unittest.AssertExistsAndLoadBean(t, &ActionTask{ID: task.ID}).Status)
assert.Equal(t, StatusRunning, unittest.AssertExistsAndLoadBean(t, &ActionRunJob{ID: job.ID}).Status)
_, err = UpdateTaskByState(t.Context(), task.RunnerID, finalState)
require.NoError(t, err)
assert.Equal(t, StatusSuccess, unittest.AssertExistsAndLoadBean(t, &ActionRunJob{ID: job.ID}).Status)
gotJob := unittest.AssertExistsAndLoadBean(t, &ActionRunJob{ID: job.ID})
assert.Equal(t, StatusSuccess, gotJob.Status)
assert.GreaterOrEqual(t, gotJob.Stopped, before)
assert.GreaterOrEqual(t, unittest.AssertExistsAndLoadBean(t, &ActionTaskStep{TaskID: task.ID}).Started, before)
}
// newRunningTaskForCancelling inserts a running run/job/task assigned to a fresh runner,