Skip to content

Commit

Permalink
chore(actions): support cron schedule task.
Browse files Browse the repository at this point in the history
Signed-off-by: Bo-Yi.Wu <[email protected]>
  • Loading branch information
appleboy committed Mar 10, 2023
1 parent dad057b commit 8650571
Show file tree
Hide file tree
Showing 10 changed files with 464 additions and 42 deletions.
3 changes: 3 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,9 @@ require (
github.com/quasoft/websspi v1.1.2
github.com/santhosh-tekuri/jsonschema/v5 v5.2.0
github.com/sergi/go-diff v1.3.1
github.com/robfig/cron/v3 v3.0.1
github.com/santhosh-tekuri/jsonschema/v5 v5.1.1
github.com/sergi/go-diff v1.2.0
github.com/shurcooL/vfsgen v0.0.0-20200824052919-0d455de96546
github.com/stretchr/testify v1.8.1
github.com/syndtr/goleveldb v1.0.0
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -1055,6 +1055,8 @@ github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUc
github.com/robertkrimen/godocdown v0.0.0-20130622164427-0bfa04905481/go.mod h1:C9WhFzY47SzYBIvzFqSvHIR6ROgDo4TtdTuRaOMjF/s=
github.com/robfig/cron v1.2.0 h1:ZjScXvvxeQ63Dbyxy76Fj3AT3Ut0aKsyd2/tl3DTMuQ=
github.com/robfig/cron v1.2.0/go.mod h1:JGuDeoQd7Z6yL4zQhZ3OPEVHB7fL6Ka6skscFHfmt2k=
github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.2.2/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
Expand Down
67 changes: 67 additions & 0 deletions models/actions/schedule.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT

package actions

import (
"context"

"code.gitea.io/gitea/models/db"
repo_model "code.gitea.io/gitea/models/repo"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/timeutil"
webhook_module "code.gitea.io/gitea/modules/webhook"
)

// ActionSchedule represents a schedule of a workflow file
type ActionSchedule struct {
ID int64
Title string
Specs []string
EntryIDs []int `xorm:"entry_ids"`
RepoID int64 `xorm:"index"`
Repo *repo_model.Repository `xorm:"-"`
OwnerID int64 `xorm:"index"`
WorkflowID string `xorm:"index"` // the name of workflow file
TriggerUserID int64
TriggerUser *user_model.User `xorm:"-"`
Ref string
CommitSHA string
Event webhook_module.HookEventType
EventPayload string `xorm:"LONGTEXT"`
Content []byte
Created timeutil.TimeStamp `xorm:"created"`
Updated timeutil.TimeStamp `xorm:"updated"`
}

func init() {
db.RegisterModel(new(ActionSchedule))
}

// CreateScheduleTask creates new schedule task.
func CreateScheduleTask(ctx context.Context, id int64, rows []*ActionSchedule) error {
for _, row := range rows {
if _, err := db.GetEngine(ctx).Insert(row); err != nil {
return err
}
}

return nil
}

func DeleteScheduleTaskByRepo(ctx context.Context, id int64) error {
if _, err := db.GetEngine(ctx).Delete(&ActionSchedule{RepoID: id}); err != nil {
return err
}
return nil
}

func UpdateSchedule(ctx context.Context, schedule *ActionSchedule, cols ...string) error {
sess := db.GetEngine(ctx).ID(schedule.ID)
if len(cols) > 0 {
sess.Cols(cols...)
}
_, err := sess.Update(schedule)

return err
}
95 changes: 95 additions & 0 deletions models/actions/schedule_list.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT

package actions

import (
"context"

"code.gitea.io/gitea/models/db"
repo_model "code.gitea.io/gitea/models/repo"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/container"

"xorm.io/builder"
)

type ScheduleList []*ActionSchedule

// GetUserIDs returns a slice of user's id
func (schedules ScheduleList) GetUserIDs() []int64 {
ids := make(container.Set[int64], len(schedules))
for _, schedule := range schedules {
ids.Add(schedule.TriggerUserID)
}
return ids.Values()
}

func (schedules ScheduleList) GetRepoIDs() []int64 {
ids := make(container.Set[int64], len(schedules))
for _, schedule := range schedules {
ids.Add(schedule.RepoID)
}
return ids.Values()
}

func (schedules ScheduleList) LoadTriggerUser(ctx context.Context) error {
userIDs := schedules.GetUserIDs()
users := make(map[int64]*user_model.User, len(userIDs))
if err := db.GetEngine(ctx).In("id", userIDs).Find(&users); err != nil {
return err
}
for _, schedule := range schedules {
if schedule.TriggerUserID == user_model.ActionsUserID {
schedule.TriggerUser = user_model.NewActionsUser()
} else {
schedule.TriggerUser = users[schedule.TriggerUserID]
}
}
return nil
}

func (schedules ScheduleList) LoadRepos() error {
repoIDs := schedules.GetRepoIDs()
repos, err := repo_model.GetRepositoriesMapByIDs(repoIDs)
if err != nil {
return err
}
for _, schedule := range schedules {
schedule.Repo = repos[schedule.RepoID]
}
return nil
}

type FindScheduleOptions struct {
db.ListOptions
RepoID int64
OwnerID int64
GetAll bool
}

func (opts FindScheduleOptions) toConds() builder.Cond {
cond := builder.NewCond()
if opts.RepoID > 0 {
cond = cond.And(builder.Eq{"repo_id": opts.RepoID})
}
if opts.OwnerID > 0 {
cond = cond.And(builder.Eq{"owner_id": opts.OwnerID})
}

return cond
}

func FindSchedules(ctx context.Context, opts FindScheduleOptions) (ScheduleList, int64, error) {
e := db.GetEngine(ctx).Where(opts.toConds())
if !opts.GetAll && opts.PageSize > 0 && opts.Page >= 1 {
e.Limit(opts.PageSize, (opts.Page-1)*opts.PageSize)
}
var schedules ScheduleList
total, err := e.Desc("id").FindAndCount(&schedules)
return schedules, total, err
}

func CountSchedules(ctx context.Context, opts FindScheduleOptions) (int64, error) {
return db.GetEngine(ctx).Where(opts.toConds()).Count(new(ActionSchedule))
}
3 changes: 3 additions & 0 deletions models/migrations/migrations.go
Original file line number Diff line number Diff line change
Expand Up @@ -467,6 +467,9 @@ var migrations = []Migration{

// v244 -> v245
NewMigration("Add NeedApproval to actions tables", v1_20.AddNeedApprovalToActionRun),

// 245 -> 246
NewMigration("Add action schedule table", v1_20.AddActionScheduleTable),
}

// GetCurrentDBVersion returns the current db version
Expand Down
34 changes: 34 additions & 0 deletions models/migrations/v1_20/v245.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT

package v1_20 //nolint

import (
"code.gitea.io/gitea/modules/timeutil"

"xorm.io/xorm"
)

func AddActionScheduleTable(x *xorm.Engine) error {
type ActionSchedule struct {
ID int64
Title string
Specs []string
EntryIDs []int `xorm:"entry_ids"`
RepoID int64 `xorm:"index"`
OwnerID int64 `xorm:"index"`
WorkflowID string `xorm:"index"` // the name of workflow file
TriggerUserID int64
Ref string
CommitSHA string
Event string
EventPayload string `xorm:"LONGTEXT"`
Content []byte
Created timeutil.TimeStamp `xorm:"created"`
Updated timeutil.TimeStamp `xorm:"updated"`
}

return x.Sync(
new(ActionSchedule),
)
}
23 changes: 18 additions & 5 deletions modules/actions/workflows.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,28 +44,41 @@ func ListWorkflows(commit *git.Commit) (git.Entries, error) {
return ret, nil
}

func DetectWorkflows(commit *git.Commit, triggedEvent webhook_module.HookEventType, payload api.Payloader) (map[string][]byte, error) {
func DetectWorkflows(
commit *git.Commit,
triggedEvent webhook_module.HookEventType,
payload api.Payloader,
) (map[string][]byte, map[string][]byte, error) {
entries, err := ListWorkflows(commit)
if err != nil {
return nil, err
return nil, nil, err
}

workflows := make(map[string][]byte, len(entries))
schedules := make(map[string][]byte, len(entries))
for _, entry := range entries {
f, err := entry.Blob().DataAsync()
if err != nil {
return nil, err
return nil, nil, err
}
content, err := io.ReadAll(f)
_ = f.Close()
if err != nil {
return nil, err
return nil, nil, err
}
workflow, err := model.ReadWorkflow(bytes.NewReader(content))
if err != nil {
log.Warn("ignore invalid workflow %q: %v", entry.Name(), err)
continue
}

// fetch all schedule event
for _, e := range workflow.On() {
if e == "schedule" {
schedules[entry.Name()] = content
}
}

events, err := jobparser.ParseRawOn(&workflow.RawOn)
if err != nil {
log.Warn("ignore invalid workflow %q: %v", entry.Name(), err)
Expand All @@ -81,7 +94,7 @@ func DetectWorkflows(commit *git.Commit, triggedEvent webhook_module.HookEventTy
}
}

return workflows, nil
return workflows, schedules, nil
}

func detectMatched(commit *git.Commit, triggedEvent webhook_module.HookEventType, payload api.Payloader, evt *jobparser.Event) bool {
Expand Down
4 changes: 4 additions & 0 deletions services/actions/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,8 @@ func Init() {
go graceful.GetManager().RunWithShutdownFns(jobEmitterQueue.Run)

notification.RegisterNotifier(NewNotifier())

// initial all schedule task
newSchedule()
resetSchedule()
}
Loading

0 comments on commit 8650571

Please sign in to comment.