code.gitea.io/gitea@v1.21.7/models/actions/schedule.go (about) 1 // Copyright 2023 The Gitea Authors. All rights reserved. 2 // SPDX-License-Identifier: MIT 3 4 package actions 5 6 import ( 7 "context" 8 "fmt" 9 "time" 10 11 "code.gitea.io/gitea/models/db" 12 repo_model "code.gitea.io/gitea/models/repo" 13 user_model "code.gitea.io/gitea/models/user" 14 "code.gitea.io/gitea/modules/timeutil" 15 webhook_module "code.gitea.io/gitea/modules/webhook" 16 17 "github.com/robfig/cron/v3" 18 ) 19 20 // ActionSchedule represents a schedule of a workflow file 21 type ActionSchedule struct { 22 ID int64 23 Title string 24 Specs []string 25 RepoID int64 `xorm:"index"` 26 Repo *repo_model.Repository `xorm:"-"` 27 OwnerID int64 `xorm:"index"` 28 WorkflowID string 29 TriggerUserID int64 30 TriggerUser *user_model.User `xorm:"-"` 31 Ref string 32 CommitSHA string 33 Event webhook_module.HookEventType 34 EventPayload string `xorm:"LONGTEXT"` 35 Content []byte 36 Created timeutil.TimeStamp `xorm:"created"` 37 Updated timeutil.TimeStamp `xorm:"updated"` 38 } 39 40 func init() { 41 db.RegisterModel(new(ActionSchedule)) 42 } 43 44 // GetSchedulesMapByIDs returns the schedules by given id slice. 45 func GetSchedulesMapByIDs(ctx context.Context, ids []int64) (map[int64]*ActionSchedule, error) { 46 schedules := make(map[int64]*ActionSchedule, len(ids)) 47 return schedules, db.GetEngine(ctx).In("id", ids).Find(&schedules) 48 } 49 50 // GetReposMapByIDs returns the repos by given id slice. 51 func GetReposMapByIDs(ctx context.Context, ids []int64) (map[int64]*repo_model.Repository, error) { 52 repos := make(map[int64]*repo_model.Repository, len(ids)) 53 return repos, db.GetEngine(ctx).In("id", ids).Find(&repos) 54 } 55 56 var cronParser = cron.NewParser(cron.Minute | cron.Hour | cron.Dom | cron.Month | cron.Dow | cron.Descriptor) 57 58 // CreateScheduleTask creates new schedule task. 59 func CreateScheduleTask(ctx context.Context, rows []*ActionSchedule) error { 60 // Return early if there are no rows to insert 61 if len(rows) == 0 { 62 return nil 63 } 64 65 // Begin transaction 66 ctx, committer, err := db.TxContext(ctx) 67 if err != nil { 68 return err 69 } 70 defer committer.Close() 71 72 // Loop through each schedule row 73 for _, row := range rows { 74 // Create new schedule row 75 if err = db.Insert(ctx, row); err != nil { 76 return err 77 } 78 79 // Loop through each schedule spec and create a new spec row 80 now := time.Now() 81 82 for _, spec := range row.Specs { 83 // Parse the spec and check for errors 84 schedule, err := cronParser.Parse(spec) 85 if err != nil { 86 continue // skip to the next spec if there's an error 87 } 88 89 // Insert the new schedule spec row 90 if err = db.Insert(ctx, &ActionScheduleSpec{ 91 RepoID: row.RepoID, 92 ScheduleID: row.ID, 93 Spec: spec, 94 Next: timeutil.TimeStamp(schedule.Next(now).Unix()), 95 }); err != nil { 96 return err 97 } 98 } 99 } 100 101 // Commit transaction 102 return committer.Commit() 103 } 104 105 func DeleteScheduleTaskByRepo(ctx context.Context, id int64) error { 106 ctx, committer, err := db.TxContext(ctx) 107 if err != nil { 108 return err 109 } 110 defer committer.Close() 111 112 if _, err := db.GetEngine(ctx).Delete(&ActionSchedule{RepoID: id}); err != nil { 113 return err 114 } 115 116 if _, err := db.GetEngine(ctx).Delete(&ActionScheduleSpec{RepoID: id}); err != nil { 117 return err 118 } 119 120 return committer.Commit() 121 } 122 123 func CleanRepoScheduleTasks(ctx context.Context, repo *repo_model.Repository) error { 124 // If actions disabled when there is schedule task, this will remove the outdated schedule tasks 125 // There is no other place we can do this because the app.ini will be changed manually 126 if err := DeleteScheduleTaskByRepo(ctx, repo.ID); err != nil { 127 return fmt.Errorf("DeleteCronTaskByRepo: %v", err) 128 } 129 // cancel running cron jobs of this repository and delete old schedules 130 if err := CancelRunningJobs( 131 ctx, 132 repo.ID, 133 repo.DefaultBranch, 134 "", 135 webhook_module.HookEventSchedule, 136 ); err != nil { 137 return fmt.Errorf("CancelRunningJobs: %v", err) 138 } 139 return nil 140 }