@@ -114,32 +114,41 @@ func UpdateRunJob(ctx context.Context, job *ActionRunJob, cond builder.Cond, col
114114 if affected != 0 && util.SliceContains(cols, "status") && job.Status.IsWaiting() {
115115 // if the status of job changes to waiting again, increase tasks version.
116116 if err := IncreaseTaskVersion(ctx, job.OwnerID, job.RepoID); err != nil {
117- return affected , err
117+ return 0 , err
118118 }
119119 }
120120
121121 if job.RunID == 0 {
122122 var err error
123123 if job, err = GetRunJobByID(ctx, job.ID); err != nil {
124- return affected , err
124+ return 0 , err
125125 }
126126 }
127127
128- jobs, err := GetRunJobsByRunID(ctx, job.RunID)
129- if err != nil {
130- return affected, err
128+ {
129+ // Other goroutines may aggregate the status of the run and update it too.
130+ // So we need load the run and its jobs before updating the run.
131+ run, err := GetRunByID(ctx, job.RunID)
132+ if err != nil {
133+ return 0, err
134+ }
135+ jobs, err := GetRunJobsByRunID(ctx, job.RunID)
136+ if err != nil {
137+ return 0, err
138+ }
139+ run.Status = aggregateJobStatus(jobs)
140+ if run.Started.IsZero() && run.Status.IsRunning() {
141+ run.Started = timeutil.TimeStampNow()
142+ }
143+ if run.Stopped.IsZero() && run.Status.IsDone() {
144+ run.Stopped = timeutil.TimeStampNow()
145+ }
146+ if err := UpdateRun(ctx, run, "status", "started", "stopped"); err != nil {
147+ return 0, fmt.Errorf("update run %d: %w", run.ID, err)
148+ }
131149 }
132150
133- runStatus := aggregateJobStatus(jobs)
134-
135- run := &ActionRun{
136- ID: job.RunID,
137- Status: runStatus,
138- }
139- if runStatus.IsDone() {
140- run.Stopped = timeutil.TimeStampNow()
141- }
142- return affected, UpdateRun(ctx, run)
151+ return affected, nil
143152}
144153
145154func aggregateJobStatus(jobs []*ActionRunJob) Status {
0 commit comments