Skip to content

Commit 8650571

Browse files
committed
chore(actions): support cron schedule task.
Signed-off-by: Bo-Yi.Wu <appleboy.tw@gmail.com>
1 parent dad057b commit 8650571

File tree

10 files changed

+464
-42
lines changed

10 files changed

+464
-42
lines changed

go.mod

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,9 @@ require (
9191
github.com/quasoft/websspi v1.1.2
9292
github.com/santhosh-tekuri/jsonschema/v5 v5.2.0
9393
github.com/sergi/go-diff v1.3.1
94+
github.com/robfig/cron/v3 v3.0.1
95+
github.com/santhosh-tekuri/jsonschema/v5 v5.1.1
96+
github.com/sergi/go-diff v1.2.0
9497
github.com/shurcooL/vfsgen v0.0.0-20200824052919-0d455de96546
9598
github.com/stretchr/testify v1.8.1
9699
github.com/syndtr/goleveldb v1.0.0

go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1055,6 +1055,8 @@ github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUc
10551055
github.com/robertkrimen/godocdown v0.0.0-20130622164427-0bfa04905481/go.mod h1:C9WhFzY47SzYBIvzFqSvHIR6ROgDo4TtdTuRaOMjF/s=
10561056
github.com/robfig/cron v1.2.0 h1:ZjScXvvxeQ63Dbyxy76Fj3AT3Ut0aKsyd2/tl3DTMuQ=
10571057
github.com/robfig/cron v1.2.0/go.mod h1:JGuDeoQd7Z6yL4zQhZ3OPEVHB7fL6Ka6skscFHfmt2k=
1058+
github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
1059+
github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
10581060
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
10591061
github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
10601062
github.com/rogpeppe/go-internal v1.2.2/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=

models/actions/schedule.go

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
// Copyright 2023 The Gitea Authors. All rights reserved.
2+
// SPDX-License-Identifier: MIT
3+
4+
package actions
5+
6+
import (
7+
"context"
8+
9+
"code.gitea.io/gitea/models/db"
10+
repo_model "code.gitea.io/gitea/models/repo"
11+
user_model "code.gitea.io/gitea/models/user"
12+
"code.gitea.io/gitea/modules/timeutil"
13+
webhook_module "code.gitea.io/gitea/modules/webhook"
14+
)
15+
16+
// ActionSchedule represents a schedule of a workflow file
17+
type ActionSchedule struct {
18+
ID int64
19+
Title string
20+
Specs []string
21+
EntryIDs []int `xorm:"entry_ids"`
22+
RepoID int64 `xorm:"index"`
23+
Repo *repo_model.Repository `xorm:"-"`
24+
OwnerID int64 `xorm:"index"`
25+
WorkflowID string `xorm:"index"` // the name of workflow file
26+
TriggerUserID int64
27+
TriggerUser *user_model.User `xorm:"-"`
28+
Ref string
29+
CommitSHA string
30+
Event webhook_module.HookEventType
31+
EventPayload string `xorm:"LONGTEXT"`
32+
Content []byte
33+
Created timeutil.TimeStamp `xorm:"created"`
34+
Updated timeutil.TimeStamp `xorm:"updated"`
35+
}
36+
37+
func init() {
38+
db.RegisterModel(new(ActionSchedule))
39+
}
40+
41+
// CreateScheduleTask creates new schedule task.
42+
func CreateScheduleTask(ctx context.Context, id int64, rows []*ActionSchedule) error {
43+
for _, row := range rows {
44+
if _, err := db.GetEngine(ctx).Insert(row); err != nil {
45+
return err
46+
}
47+
}
48+
49+
return nil
50+
}
51+
52+
func DeleteScheduleTaskByRepo(ctx context.Context, id int64) error {
53+
if _, err := db.GetEngine(ctx).Delete(&ActionSchedule{RepoID: id}); err != nil {
54+
return err
55+
}
56+
return nil
57+
}
58+
59+
func UpdateSchedule(ctx context.Context, schedule *ActionSchedule, cols ...string) error {
60+
sess := db.GetEngine(ctx).ID(schedule.ID)
61+
if len(cols) > 0 {
62+
sess.Cols(cols...)
63+
}
64+
_, err := sess.Update(schedule)
65+
66+
return err
67+
}

models/actions/schedule_list.go

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
// Copyright 2023 The Gitea Authors. All rights reserved.
2+
// SPDX-License-Identifier: MIT
3+
4+
package actions
5+
6+
import (
7+
"context"
8+
9+
"code.gitea.io/gitea/models/db"
10+
repo_model "code.gitea.io/gitea/models/repo"
11+
user_model "code.gitea.io/gitea/models/user"
12+
"code.gitea.io/gitea/modules/container"
13+
14+
"xorm.io/builder"
15+
)
16+
17+
type ScheduleList []*ActionSchedule
18+
19+
// GetUserIDs returns a slice of user's id
20+
func (schedules ScheduleList) GetUserIDs() []int64 {
21+
ids := make(container.Set[int64], len(schedules))
22+
for _, schedule := range schedules {
23+
ids.Add(schedule.TriggerUserID)
24+
}
25+
return ids.Values()
26+
}
27+
28+
func (schedules ScheduleList) GetRepoIDs() []int64 {
29+
ids := make(container.Set[int64], len(schedules))
30+
for _, schedule := range schedules {
31+
ids.Add(schedule.RepoID)
32+
}
33+
return ids.Values()
34+
}
35+
36+
func (schedules ScheduleList) LoadTriggerUser(ctx context.Context) error {
37+
userIDs := schedules.GetUserIDs()
38+
users := make(map[int64]*user_model.User, len(userIDs))
39+
if err := db.GetEngine(ctx).In("id", userIDs).Find(&users); err != nil {
40+
return err
41+
}
42+
for _, schedule := range schedules {
43+
if schedule.TriggerUserID == user_model.ActionsUserID {
44+
schedule.TriggerUser = user_model.NewActionsUser()
45+
} else {
46+
schedule.TriggerUser = users[schedule.TriggerUserID]
47+
}
48+
}
49+
return nil
50+
}
51+
52+
func (schedules ScheduleList) LoadRepos() error {
53+
repoIDs := schedules.GetRepoIDs()
54+
repos, err := repo_model.GetRepositoriesMapByIDs(repoIDs)
55+
if err != nil {
56+
return err
57+
}
58+
for _, schedule := range schedules {
59+
schedule.Repo = repos[schedule.RepoID]
60+
}
61+
return nil
62+
}
63+
64+
type FindScheduleOptions struct {
65+
db.ListOptions
66+
RepoID int64
67+
OwnerID int64
68+
GetAll bool
69+
}
70+
71+
func (opts FindScheduleOptions) toConds() builder.Cond {
72+
cond := builder.NewCond()
73+
if opts.RepoID > 0 {
74+
cond = cond.And(builder.Eq{"repo_id": opts.RepoID})
75+
}
76+
if opts.OwnerID > 0 {
77+
cond = cond.And(builder.Eq{"owner_id": opts.OwnerID})
78+
}
79+
80+
return cond
81+
}
82+
83+
func FindSchedules(ctx context.Context, opts FindScheduleOptions) (ScheduleList, int64, error) {
84+
e := db.GetEngine(ctx).Where(opts.toConds())
85+
if !opts.GetAll && opts.PageSize > 0 && opts.Page >= 1 {
86+
e.Limit(opts.PageSize, (opts.Page-1)*opts.PageSize)
87+
}
88+
var schedules ScheduleList
89+
total, err := e.Desc("id").FindAndCount(&schedules)
90+
return schedules, total, err
91+
}
92+
93+
func CountSchedules(ctx context.Context, opts FindScheduleOptions) (int64, error) {
94+
return db.GetEngine(ctx).Where(opts.toConds()).Count(new(ActionSchedule))
95+
}

models/migrations/migrations.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -467,6 +467,9 @@ var migrations = []Migration{
467467

468468
// v244 -> v245
469469
NewMigration("Add NeedApproval to actions tables", v1_20.AddNeedApprovalToActionRun),
470+
471+
// 245 -> 246
472+
NewMigration("Add action schedule table", v1_20.AddActionScheduleTable),
470473
}
471474

472475
// GetCurrentDBVersion returns the current db version

models/migrations/v1_20/v245.go

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// Copyright 2023 The Gitea Authors. All rights reserved.
2+
// SPDX-License-Identifier: MIT
3+
4+
package v1_20 //nolint
5+
6+
import (
7+
"code.gitea.io/gitea/modules/timeutil"
8+
9+
"xorm.io/xorm"
10+
)
11+
12+
func AddActionScheduleTable(x *xorm.Engine) error {
13+
type ActionSchedule struct {
14+
ID int64
15+
Title string
16+
Specs []string
17+
EntryIDs []int `xorm:"entry_ids"`
18+
RepoID int64 `xorm:"index"`
19+
OwnerID int64 `xorm:"index"`
20+
WorkflowID string `xorm:"index"` // the name of workflow file
21+
TriggerUserID int64
22+
Ref string
23+
CommitSHA string
24+
Event string
25+
EventPayload string `xorm:"LONGTEXT"`
26+
Content []byte
27+
Created timeutil.TimeStamp `xorm:"created"`
28+
Updated timeutil.TimeStamp `xorm:"updated"`
29+
}
30+
31+
return x.Sync(
32+
new(ActionSchedule),
33+
)
34+
}

modules/actions/workflows.go

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -44,28 +44,41 @@ func ListWorkflows(commit *git.Commit) (git.Entries, error) {
4444
return ret, nil
4545
}
4646

47-
func DetectWorkflows(commit *git.Commit, triggedEvent webhook_module.HookEventType, payload api.Payloader) (map[string][]byte, error) {
47+
func DetectWorkflows(
48+
commit *git.Commit,
49+
triggedEvent webhook_module.HookEventType,
50+
payload api.Payloader,
51+
) (map[string][]byte, map[string][]byte, error) {
4852
entries, err := ListWorkflows(commit)
4953
if err != nil {
50-
return nil, err
54+
return nil, nil, err
5155
}
5256

5357
workflows := make(map[string][]byte, len(entries))
58+
schedules := make(map[string][]byte, len(entries))
5459
for _, entry := range entries {
5560
f, err := entry.Blob().DataAsync()
5661
if err != nil {
57-
return nil, err
62+
return nil, nil, err
5863
}
5964
content, err := io.ReadAll(f)
6065
_ = f.Close()
6166
if err != nil {
62-
return nil, err
67+
return nil, nil, err
6368
}
6469
workflow, err := model.ReadWorkflow(bytes.NewReader(content))
6570
if err != nil {
6671
log.Warn("ignore invalid workflow %q: %v", entry.Name(), err)
6772
continue
6873
}
74+
75+
// fetch all schedule event
76+
for _, e := range workflow.On() {
77+
if e == "schedule" {
78+
schedules[entry.Name()] = content
79+
}
80+
}
81+
6982
events, err := jobparser.ParseRawOn(&workflow.RawOn)
7083
if err != nil {
7184
log.Warn("ignore invalid workflow %q: %v", entry.Name(), err)
@@ -81,7 +94,7 @@ func DetectWorkflows(commit *git.Commit, triggedEvent webhook_module.HookEventTy
8194
}
8295
}
8396

84-
return workflows, nil
97+
return workflows, schedules, nil
8598
}
8699

87100
func detectMatched(commit *git.Commit, triggedEvent webhook_module.HookEventType, payload api.Payloader, evt *jobparser.Event) bool {

services/actions/init.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,4 +19,8 @@ func Init() {
1919
go graceful.GetManager().RunWithShutdownFns(jobEmitterQueue.Run)
2020

2121
notification.RegisterNotifier(NewNotifier())
22+
23+
// initial all schedule task
24+
newSchedule()
25+
resetSchedule()
2226
}

0 commit comments

Comments
 (0)