@@ -17,6 +17,7 @@ import (
1717 webhook_module "forgejo.org/modules/webhook"
1818
1919 "github.com/nektos/act/pkg/jobparser"
20+ "xorm.io/builder"
2021)
2122
2223// StartScheduleTasks start the task
@@ -55,7 +56,7 @@ func startTasks(ctx context.Context) error {
5556 // cancel running jobs if the event is push
5657 if row .Schedule .Event == webhook_module .HookEventPush {
5758 // cancel running jobs of the same workflow
58- if err := actions_model . CancelPreviousJobs (
59+ if err := CancelPreviousJobs (
5960 ctx ,
6061 row .RepoID ,
6162 row .Schedule .Ref ,
@@ -152,3 +153,93 @@ func CreateScheduleTask(ctx context.Context, cron *actions_model.ActionSchedule)
152153 // Return nil if no errors occurred
153154 return nil
154155}
156+
157+ // CancelPreviousJobs cancels all previous jobs of the same repository, reference, workflow, and event.
158+ // It's useful when a new run is triggered, and all previous runs needn't be continued anymore.
159+ func CancelPreviousJobs (ctx context.Context , repoID int64 , ref , workflowID string , event webhook_module.HookEventType ) error {
160+ // Find all runs in the specified repository, reference, and workflow with non-final status
161+ runs , total , err := db .FindAndCount [actions_model.ActionRun ](ctx , actions_model.FindRunOptions {
162+ RepoID : repoID ,
163+ Ref : ref ,
164+ WorkflowID : workflowID ,
165+ TriggerEvent : event ,
166+ Status : []actions_model.Status {actions_model .StatusRunning , actions_model .StatusWaiting , actions_model .StatusBlocked },
167+ })
168+ if err != nil {
169+ return err
170+ }
171+
172+ // If there are no runs found, there's no need to proceed with cancellation, so return nil.
173+ if total == 0 {
174+ return nil
175+ }
176+
177+ // Iterate over each found run and cancel its associated jobs.
178+ for _ , run := range runs {
179+ // Find all jobs associated with the current run.
180+ jobs , err := db .Find [actions_model.ActionRunJob ](ctx , actions_model.FindRunJobOptions {
181+ RunID : run .ID ,
182+ })
183+ if err != nil {
184+ return err
185+ }
186+
187+ // Iterate over each job and attempt to cancel it.
188+ for _ , job := range jobs {
189+ // Skip jobs that are already in a terminal state (completed, cancelled, etc.).
190+ status := job .Status
191+ if status .IsDone () {
192+ continue
193+ }
194+
195+ // If the job has no associated task (probably an error), set its status to 'Cancelled' and stop it.
196+ if job .TaskID == 0 {
197+ job .Status = actions_model .StatusCancelled
198+ job .Stopped = timeutil .TimeStampNow ()
199+
200+ // Update the job's status and stopped time in the database.
201+ n , err := actions_model .UpdateRunJob (ctx , job , builder.Eq {"task_id" : 0 }, "status" , "stopped" )
202+ if err != nil {
203+ return err
204+ }
205+
206+ // If the update affected 0 rows, it means the job has changed in the meantime, so we need to try again.
207+ if n == 0 {
208+ return fmt .Errorf ("job has changed, try again" )
209+ }
210+
211+ // Continue with the next job.
212+ continue
213+ }
214+
215+ // If the job has an associated task, try to stop the task, effectively cancelling the job.
216+ if err := StopTask (ctx , job .TaskID , actions_model .StatusCancelled ); err != nil {
217+ return err
218+ }
219+ }
220+ }
221+
222+ // Return nil to indicate successful cancellation of all running and waiting jobs.
223+ return nil
224+ }
225+
226+ func CleanRepoScheduleTasks (ctx context.Context , repo * repo_model.Repository , cancelPreviousJobs bool ) error {
227+ // If actions disabled when there is schedule task, this will remove the outdated schedule tasks
228+ // There is no other place we can do this because the app.ini will be changed manually
229+ if err := actions_model .DeleteScheduleTaskByRepo (ctx , repo .ID ); err != nil {
230+ return fmt .Errorf ("DeleteCronTaskByRepo: %v" , err )
231+ }
232+ if cancelPreviousJobs {
233+ // cancel running cron jobs of this repository and delete old schedules
234+ if err := CancelPreviousJobs (
235+ ctx ,
236+ repo .ID ,
237+ repo .DefaultBranch ,
238+ "" ,
239+ webhook_module .HookEventSchedule ,
240+ ); err != nil {
241+ return fmt .Errorf ("CancelPreviousJobs: %v" , err )
242+ }
243+ }
244+ return nil
245+ }
0 commit comments