Replace gogs/cron with go-co-op/gocron (#25977)

Replace `github.com/gogs/cron` with `github.com/go-co-op/gocron` as the
former package is not maintained for many years.

---------

Co-authored-by: delvh <dev.lh@web.de>
This commit is contained in:
Chongyi Zheng 2023-07-23 23:13:41 -05:00 committed by GitHub
parent f3d41c61eb
commit f2138d6968
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 123 additions and 22 deletions

View file

@ -14,10 +14,10 @@ import (
"code.gitea.io/gitea/modules/sync"
"code.gitea.io/gitea/modules/translation"
"github.com/gogs/cron"
"github.com/go-co-op/gocron"
)
var c = cron.New()
var scheduler = gocron.NewScheduler(time.Local)
// Prevent duplicate running tasks.
var taskStatusTable = sync.NewStatusTable()
@ -39,11 +39,11 @@ func NewContext(original context.Context) {
}
}
c.Start()
scheduler.StartAsync()
started = true
lock.Unlock()
graceful.GetManager().RunAtShutdown(context.Background(), func() {
c.Stop()
scheduler.Stop()
lock.Lock()
started = false
lock.Unlock()
@ -77,13 +77,20 @@ type TaskTable []*TaskTableRow
// ListTasks returns all running cron tasks.
func ListTasks() TaskTable {
entries := c.Entries()
eMap := map[string]*cron.Entry{}
for _, e := range entries {
eMap[e.Description] = e
jobs := scheduler.Jobs()
jobMap := map[string]*gocron.Job{}
for _, job := range jobs {
// the first tag is the task name
tags := job.Tags()
if len(tags) == 0 { // should never happen
continue
}
jobMap[job.Tags()[0]] = job
}
lock.Lock()
defer lock.Unlock()
tTable := make([]*TaskTableRow, 0, len(tasks))
for _, task := range tasks {
spec := "-"
@ -91,10 +98,13 @@ func ListTasks() TaskTable {
next time.Time
prev time.Time
)
if e, ok := eMap[task.Name]; ok {
spec = e.Spec
next = e.Next
prev = e.Prev
if e, ok := jobMap[task.Name]; ok {
tags := e.Tags()
if len(tags) > 1 {
spec = tags[1] // the second tag is the task spec
}
next = e.NextRun()
prev = e.PreviousRun()
}
task.lock.Lock()
tTable = append(tTable, &TaskTableRow{

View file

@ -7,6 +7,7 @@ import (
"context"
"fmt"
"reflect"
"strings"
"sync"
"code.gitea.io/gitea/models/db"
@ -176,8 +177,7 @@ func RegisterTask(name string, config Config, fun func(context.Context, *user_mo
if config.IsEnabled() {
// We cannot use the entry return as there is no way to lock it
if _, err = c.AddJob(name, config.GetSchedule(), task); err != nil {
log.Error("Unable to register cron task with name: %s Error: %v", name, err)
if err := addTaskToScheduler(task); err != nil {
return err
}
}
@ -199,3 +199,21 @@ func RegisterTaskFatal(name string, config Config, fun func(context.Context, *us
log.Fatal("Unable to register cron task %s Error: %v", name, err)
}
}
func addTaskToScheduler(task *Task) error {
tags := []string{task.Name, task.config.GetSchedule()} // name and schedule can't be get from job, so we add them as tag
if scheduleHasSeconds(task.config.GetSchedule()) {
scheduler = scheduler.CronWithSeconds(task.config.GetSchedule())
} else {
scheduler = scheduler.Cron(task.config.GetSchedule())
}
if _, err := scheduler.Tag(tags...).Do(task.Run); err != nil {
log.Error("Unable to register cron task with name: %s Error: %v", task.Name, err)
return err
}
return nil
}
func scheduleHasSeconds(schedule string) bool {
return len(strings.Fields(schedule)) >= 6
}

View file

@ -0,0 +1,61 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package cron
import (
"strconv"
"testing"
"github.com/stretchr/testify/assert"
)
func TestAddTaskToScheduler(t *testing.T) {
assert.Len(t, scheduler.Jobs(), 0)
defer scheduler.Clear()
// no seconds
err := addTaskToScheduler(&Task{
Name: "task 1",
config: &BaseConfig{
Schedule: "5 4 * * *",
},
})
assert.NoError(t, err)
assert.Len(t, scheduler.Jobs(), 1)
assert.Equal(t, "task 1", scheduler.Jobs()[0].Tags()[0])
assert.Equal(t, "5 4 * * *", scheduler.Jobs()[0].Tags()[1])
// with seconds
err = addTaskToScheduler(&Task{
Name: "task 2",
config: &BaseConfig{
Schedule: "30 5 4 * * *",
},
})
assert.NoError(t, err)
assert.Len(t, scheduler.Jobs(), 2)
assert.Equal(t, "task 2", scheduler.Jobs()[1].Tags()[0])
assert.Equal(t, "30 5 4 * * *", scheduler.Jobs()[1].Tags()[1])
}
func TestScheduleHasSeconds(t *testing.T) {
tests := []struct {
schedule string
hasSecond bool
}{
{"* * * * * *", true},
{"* * * * *", false},
{"5 4 * * *", false},
{"5 4 * * *", false},
{"5,8 4 * * *", false},
{"* * * * * *", true},
{"5,8 4 * * *", false},
}
for i, test := range tests {
t.Run(strconv.Itoa(i), func(t *testing.T) {
assert.Equal(t, test.hasSecond, scheduleHasSeconds(test.schedule))
})
}
}