@@ -7,12 +7,17 @@ import (
77 "context"
88 "fmt"
99
10+ actions_model "code.gitea.io/gitea/models/actions"
1011 "code.gitea.io/gitea/models/db"
1112 repo_model "code.gitea.io/gitea/models/repo"
1213 unit_model "code.gitea.io/gitea/models/unit"
1314 "code.gitea.io/gitea/modules/log"
1415 "code.gitea.io/gitea/modules/optional"
16+ "code.gitea.io/gitea/modules/setting"
17+ "code.gitea.io/gitea/modules/timeutil"
1518 repo_service "code.gitea.io/gitea/services/repository"
19+
20+ "xorm.io/builder"
1621)
1722
1823func disableMirrorActionsUnit (ctx context.Context , logger log.Logger , autofix bool ) error {
@@ -59,6 +64,95 @@ func disableMirrorActionsUnit(ctx context.Context, logger log.Logger, autofix bo
5964 return nil
6065}
6166
67+ func fixUnfinishedRunStatus (ctx context.Context , logger log.Logger , autofix bool ) error {
68+ total := 0
69+ inconsistent := 0
70+ fixed := 0
71+
72+ cond := builder .In ("status" , []actions_model.Status {
73+ actions_model .StatusWaiting ,
74+ actions_model .StatusRunning ,
75+ actions_model .StatusBlocked ,
76+ }).And (builder.Lt {"updated" : timeutil .TimeStampNow ().AddDuration (- setting .Actions .ZombieTaskTimeout )})
77+
78+ err := db .Iterate (
79+ ctx ,
80+ cond ,
81+ func (ctx context.Context , run * actions_model.ActionRun ) error {
82+ total ++
83+
84+ jobs , err := actions_model .GetRunJobsByRunID (ctx , run .ID )
85+ if err != nil {
86+ return fmt .Errorf ("GetRunJobsByRunID: %w" , err )
87+ }
88+ expected := actions_model .AggregateJobStatus (jobs )
89+ if expected == run .Status {
90+ return nil
91+ }
92+
93+ inconsistent ++
94+ logger .Warn ("Run %d (repo_id=%d, index=%d) has status %s, expected %s" , run .ID , run .RepoID , run .Index , run .Status , expected )
95+
96+ if ! autofix {
97+ return nil
98+ }
99+
100+ run .Started , run .Stopped = getRunTimestampsFromJobs (run , expected , jobs )
101+ run .Status = expected
102+
103+ if err := actions_model .UpdateRun (ctx , run , "status" , "started" , "stopped" ); err != nil {
104+ return fmt .Errorf ("UpdateRun: %w" , err )
105+ }
106+ fixed ++
107+
108+ return nil
109+ },
110+ )
111+ if err != nil {
112+ logger .Critical ("Unable to iterate unfinished runs: %v" , err )
113+ return err
114+ }
115+
116+ if inconsistent == 0 {
117+ logger .Info ("Checked %d unfinished runs; all statuses are consistent." , total )
118+ return nil
119+ }
120+
121+ if autofix {
122+ logger .Info ("Checked %d unfinished runs; fixed %d of %d runs." , total , fixed , inconsistent )
123+ } else {
124+ logger .Warn ("Checked %d unfinished runs; found %d runs need to be fixed" , total , inconsistent )
125+ }
126+
127+ return nil
128+ }
129+
130+ func getRunTimestampsFromJobs (run * actions_model.ActionRun , newStatus actions_model.Status , jobs actions_model.ActionJobList ) (started , stopped timeutil.TimeStamp ) {
131+ started = run .Started
132+ if (newStatus .IsRunning () || newStatus .IsDone ()) && started .IsZero () {
133+ var earliest timeutil.TimeStamp
134+ for _ , job := range jobs {
135+ if job .Started > 0 && (earliest .IsZero () || job .Started < earliest ) {
136+ earliest = job .Started
137+ }
138+ }
139+ started = earliest
140+ }
141+
142+ stopped = run .Stopped
143+ if newStatus .IsDone () && stopped .IsZero () {
144+ var latest timeutil.TimeStamp
145+ for _ , job := range jobs {
146+ if job .Stopped > latest {
147+ latest = job .Stopped
148+ }
149+ }
150+ stopped = latest
151+ }
152+
153+ return started , stopped
154+ }
155+
62156func init () {
63157 Register (& Check {
64158 Title : "Disable the actions unit for all mirrors" ,
@@ -67,4 +161,11 @@ func init() {
67161 Run : disableMirrorActionsUnit ,
68162 Priority : 9 ,
69163 })
164+ Register (& Check {
165+ Title : "Fix inconsistent status for unfinished actions runs" ,
166+ Name : "fix-actions-unfinished-run-status" ,
167+ IsDefault : false ,
168+ Run : fixUnfinishedRunStatus ,
169+ Priority : 9 ,
170+ })
70171}
0 commit comments