2023-08-24 03:06:51 +00:00
|
|
|
// Copyright 2023 The Gitea Authors. All rights reserved.
|
|
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
|
|
|
|
package actions
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"fmt"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
actions_model "code.gitea.io/gitea/models/actions"
|
|
|
|
"code.gitea.io/gitea/models/db"
|
2024-01-31 14:55:12 +00:00
|
|
|
repo_model "code.gitea.io/gitea/models/repo"
|
2023-09-07 02:25:46 +00:00
|
|
|
"code.gitea.io/gitea/models/unit"
|
2023-08-24 03:06:51 +00:00
|
|
|
"code.gitea.io/gitea/modules/log"
|
|
|
|
"code.gitea.io/gitea/modules/timeutil"
|
|
|
|
webhook_module "code.gitea.io/gitea/modules/webhook"
|
|
|
|
|
|
|
|
"github.com/nektos/act/pkg/jobparser"
|
|
|
|
)
|
|
|
|
|
|
|
|
// StartScheduleTasks start the task
|
|
|
|
func StartScheduleTasks(ctx context.Context) error {
|
|
|
|
return startTasks(ctx)
|
|
|
|
}
|
|
|
|
|
|
|
|
// startTasks retrieves specifications in pages, creates a schedule task for each specification,
|
|
|
|
// and updates the specification's next run time and previous run time.
|
|
|
|
// The function returns an error if there's an issue with finding or updating the specifications.
|
|
|
|
func startTasks(ctx context.Context) error {
|
|
|
|
// Set the page size
|
|
|
|
pageSize := 50
|
|
|
|
|
|
|
|
// Retrieve specs in pages until all specs have been retrieved
|
|
|
|
now := time.Now()
|
|
|
|
for page := 1; ; page++ {
|
|
|
|
// Retrieve the specs for the current page
|
|
|
|
specs, _, err := actions_model.FindSpecs(ctx, actions_model.FindSpecOptions{
|
|
|
|
ListOptions: db.ListOptions{
|
|
|
|
Page: page,
|
|
|
|
PageSize: pageSize,
|
|
|
|
},
|
|
|
|
Next: now.Unix(),
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("find specs: %w", err)
|
|
|
|
}
|
|
|
|
|
2023-10-11 04:24:07 +00:00
|
|
|
if err := specs.LoadRepos(ctx); err != nil {
|
2023-09-07 02:25:46 +00:00
|
|
|
return fmt.Errorf("LoadRepos: %w", err)
|
|
|
|
}
|
|
|
|
|
2023-08-24 03:06:51 +00:00
|
|
|
// Loop through each spec and create a schedule task for it
|
|
|
|
for _, row := range specs {
|
|
|
|
// cancel running jobs if the event is push
|
|
|
|
if row.Schedule.Event == webhook_module.HookEventPush {
|
|
|
|
// cancel running jobs of the same workflow
|
2024-03-21 07:01:35 +00:00
|
|
|
if err := actions_model.CancelPreviousJobs(
|
2023-08-24 03:06:51 +00:00
|
|
|
ctx,
|
|
|
|
row.RepoID,
|
|
|
|
row.Schedule.Ref,
|
|
|
|
row.Schedule.WorkflowID,
|
2024-01-12 21:50:38 +00:00
|
|
|
webhook_module.HookEventSchedule,
|
2023-08-24 03:06:51 +00:00
|
|
|
); err != nil {
|
2024-03-21 07:01:35 +00:00
|
|
|
log.Error("CancelPreviousJobs: %v", err)
|
2023-08-24 03:06:51 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-04-01 13:48:14 +00:00
|
|
|
if row.Repo.IsArchived {
|
|
|
|
// Skip if the repo is archived
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2024-01-31 14:55:12 +00:00
|
|
|
cfg, err := row.Repo.GetUnit(ctx, unit.TypeActions)
|
|
|
|
if err != nil {
|
|
|
|
if repo_model.IsErrUnitTypeNotExist(err) {
|
|
|
|
// Skip the actions unit of this repo is disabled.
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
return fmt.Errorf("GetUnit: %w", err)
|
|
|
|
}
|
|
|
|
if cfg.ActionsConfig().IsWorkflowDisabled(row.Schedule.WorkflowID) {
|
2023-09-07 02:25:46 +00:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2023-08-24 03:06:51 +00:00
|
|
|
if err := CreateScheduleTask(ctx, row.Schedule); err != nil {
|
|
|
|
log.Error("CreateScheduleTask: %v", err)
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Parse the spec
|
|
|
|
schedule, err := row.Parse()
|
|
|
|
if err != nil {
|
|
|
|
log.Error("Parse: %v", err)
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Update the spec's next run time and previous run time
|
|
|
|
row.Prev = row.Next
|
|
|
|
row.Next = timeutil.TimeStamp(schedule.Next(now.Add(1 * time.Minute)).Unix())
|
|
|
|
if err := actions_model.UpdateScheduleSpec(ctx, row, "prev", "next"); err != nil {
|
|
|
|
log.Error("UpdateScheduleSpec: %v", err)
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Stop if all specs have been retrieved
|
|
|
|
if len(specs) < pageSize {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// CreateScheduleTask creates a scheduled task from a cron action schedule.
|
|
|
|
// It creates an action run based on the schedule, inserts it into the database, and creates commit statuses for each job.
|
|
|
|
func CreateScheduleTask(ctx context.Context, cron *actions_model.ActionSchedule) error {
|
|
|
|
// Create a new action run based on the schedule
|
|
|
|
run := &actions_model.ActionRun{
|
|
|
|
Title: cron.Title,
|
|
|
|
RepoID: cron.RepoID,
|
|
|
|
OwnerID: cron.OwnerID,
|
|
|
|
WorkflowID: cron.WorkflowID,
|
|
|
|
TriggerUserID: cron.TriggerUserID,
|
|
|
|
Ref: cron.Ref,
|
|
|
|
CommitSHA: cron.CommitSHA,
|
|
|
|
Event: cron.Event,
|
|
|
|
EventPayload: cron.EventPayload,
|
2024-01-12 21:50:38 +00:00
|
|
|
TriggerEvent: string(webhook_module.HookEventSchedule),
|
2023-09-08 15:01:19 +00:00
|
|
|
ScheduleID: cron.ID,
|
2023-08-24 03:06:51 +00:00
|
|
|
Status: actions_model.StatusWaiting,
|
|
|
|
}
|
|
|
|
|
2024-04-06 19:54:53 +00:00
|
|
|
vars, err := actions_model.GetVariablesOfRun(ctx, run)
|
|
|
|
if err != nil {
|
2024-04-23 18:55:25 +00:00
|
|
|
log.Error("GetVariablesOfRun: %v", err)
|
|
|
|
return err
|
2024-04-06 19:54:53 +00:00
|
|
|
}
|
|
|
|
|
2023-08-24 03:06:51 +00:00
|
|
|
// Parse the workflow specification from the cron schedule
|
2024-04-06 19:54:53 +00:00
|
|
|
workflows, err := jobparser.Parse(cron.Content, jobparser.WithVars(vars))
|
2023-08-24 03:06:51 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Insert the action run and its associated jobs into the database
|
|
|
|
if err := actions_model.InsertRun(ctx, run, workflows); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Return nil if no errors occurred
|
|
|
|
return nil
|
|
|
|
}
|