code.gitea.io/gitea@v1.21.7/services/actions/schedule_tasks.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 actions_model "code.gitea.io/gitea/models/actions" 12 "code.gitea.io/gitea/models/db" 13 repo_model "code.gitea.io/gitea/models/repo" 14 "code.gitea.io/gitea/models/unit" 15 "code.gitea.io/gitea/modules/log" 16 "code.gitea.io/gitea/modules/timeutil" 17 webhook_module "code.gitea.io/gitea/modules/webhook" 18 19 "github.com/nektos/act/pkg/jobparser" 20 ) 21 22 // StartScheduleTasks start the task 23 func StartScheduleTasks(ctx context.Context) error { 24 return startTasks(ctx) 25 } 26 27 // startTasks retrieves specifications in pages, creates a schedule task for each specification, 28 // and updates the specification's next run time and previous run time. 29 // The function returns an error if there's an issue with finding or updating the specifications. 30 func startTasks(ctx context.Context) error { 31 // Set the page size 32 pageSize := 50 33 34 // Retrieve specs in pages until all specs have been retrieved 35 now := time.Now() 36 for page := 1; ; page++ { 37 // Retrieve the specs for the current page 38 specs, _, err := actions_model.FindSpecs(ctx, actions_model.FindSpecOptions{ 39 ListOptions: db.ListOptions{ 40 Page: page, 41 PageSize: pageSize, 42 }, 43 Next: now.Unix(), 44 }) 45 if err != nil { 46 return fmt.Errorf("find specs: %w", err) 47 } 48 49 if err := specs.LoadRepos(); err != nil { 50 return fmt.Errorf("LoadRepos: %w", err) 51 } 52 53 // Loop through each spec and create a schedule task for it 54 for _, row := range specs { 55 // cancel running jobs if the event is push 56 if row.Schedule.Event == webhook_module.HookEventPush { 57 // cancel running jobs of the same workflow 58 if err := actions_model.CancelRunningJobs( 59 ctx, 60 row.RepoID, 61 row.Schedule.Ref, 62 row.Schedule.WorkflowID, 63 webhook_module.HookEventSchedule, 64 ); err != nil { 65 log.Error("CancelRunningJobs: %v", err) 66 } 67 } 68 69 cfg, err := row.Repo.GetUnit(ctx, unit.TypeActions) 70 if err != nil { 71 if repo_model.IsErrUnitTypeNotExist(err) { 72 // Skip the actions unit of this repo is disabled. 73 continue 74 } 75 return fmt.Errorf("GetUnit: %w", err) 76 } 77 if cfg.ActionsConfig().IsWorkflowDisabled(row.Schedule.WorkflowID) { 78 continue 79 } 80 81 if err := CreateScheduleTask(ctx, row.Schedule); err != nil { 82 log.Error("CreateScheduleTask: %v", err) 83 return err 84 } 85 86 // Parse the spec 87 schedule, err := row.Parse() 88 if err != nil { 89 log.Error("Parse: %v", err) 90 return err 91 } 92 93 // Update the spec's next run time and previous run time 94 row.Prev = row.Next 95 row.Next = timeutil.TimeStamp(schedule.Next(now.Add(1 * time.Minute)).Unix()) 96 if err := actions_model.UpdateScheduleSpec(ctx, row, "prev", "next"); err != nil { 97 log.Error("UpdateScheduleSpec: %v", err) 98 return err 99 } 100 } 101 102 // Stop if all specs have been retrieved 103 if len(specs) < pageSize { 104 break 105 } 106 } 107 108 return nil 109 } 110 111 // CreateScheduleTask creates a scheduled task from a cron action schedule. 112 // It creates an action run based on the schedule, inserts it into the database, and creates commit statuses for each job. 113 func CreateScheduleTask(ctx context.Context, cron *actions_model.ActionSchedule) error { 114 // Create a new action run based on the schedule 115 run := &actions_model.ActionRun{ 116 Title: cron.Title, 117 RepoID: cron.RepoID, 118 OwnerID: cron.OwnerID, 119 WorkflowID: cron.WorkflowID, 120 TriggerUserID: cron.TriggerUserID, 121 Ref: cron.Ref, 122 CommitSHA: cron.CommitSHA, 123 Event: cron.Event, 124 EventPayload: cron.EventPayload, 125 TriggerEvent: string(webhook_module.HookEventSchedule), 126 ScheduleID: cron.ID, 127 Status: actions_model.StatusWaiting, 128 } 129 130 // Parse the workflow specification from the cron schedule 131 workflows, err := jobparser.Parse(cron.Content) 132 if err != nil { 133 return err 134 } 135 136 // Insert the action run and its associated jobs into the database 137 if err := actions_model.InsertRun(ctx, run, workflows); err != nil { 138 return err 139 } 140 141 // Return nil if no errors occurred 142 return nil 143 }