1050d7a78f
- Currently in the cron tasks, the 'Previous Time' only displays the previous time of when the cron library executes the function, but not any of the manual executions of the task. - Store the last run's time in memory in the Task struct and use that, when that time is later than time that the cron library has executed this task. - This ensures that if an instance admin manually starts a task, there's feedback that this task is/has been run, because the task might be run that quick, that the status icon already has been changed to an checkmark, - Tasks that are executed at startup now reflect this as well, as the time of the execution of that task on startup is now being shown as 'Previous Time'. - Added integration tests for the API part, which is easier to test because querying the HTML table of cron tasks is non-trivial. - Resolves https://codeberg.org/forgejo/forgejo/issues/949 (cherry picked from commit fd34fdac1408ece6b7d9fe6a76501ed9a45d06fa) --------- Co-authored-by: Gusted <postmaster@gusted.xyz> Co-authored-by: KN4CK3R <admin@oldschoolhack.me> Co-authored-by: silverwind <me@silverwind.io>
132 lines
3 KiB
Go
132 lines
3 KiB
Go
// Copyright 2014 The Gogs Authors. All rights reserved.
|
|
// Copyright 2019 The Gitea Authors. All rights reserved.
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
package cron
|
|
|
|
import (
|
|
"context"
|
|
"runtime/pprof"
|
|
"time"
|
|
|
|
"code.gitea.io/gitea/modules/graceful"
|
|
"code.gitea.io/gitea/modules/process"
|
|
"code.gitea.io/gitea/modules/sync"
|
|
"code.gitea.io/gitea/modules/translation"
|
|
|
|
"github.com/go-co-op/gocron"
|
|
)
|
|
|
|
var scheduler = gocron.NewScheduler(time.Local)
|
|
|
|
// Prevent duplicate running tasks.
|
|
var taskStatusTable = sync.NewStatusTable()
|
|
|
|
// NewContext begins cron tasks
|
|
// Each cron task is run within the shutdown context as a running server
|
|
// AtShutdown the cron server is stopped
|
|
func NewContext(original context.Context) {
|
|
defer pprof.SetGoroutineLabels(original)
|
|
_, _, finished := process.GetManager().AddTypedContext(graceful.GetManager().ShutdownContext(), "Service: Cron", process.SystemProcessType, true)
|
|
initBasicTasks()
|
|
initExtendedTasks()
|
|
initActionsTasks()
|
|
|
|
lock.Lock()
|
|
for _, task := range tasks {
|
|
if task.IsEnabled() && task.DoRunAtStart() {
|
|
go task.Run()
|
|
}
|
|
}
|
|
|
|
scheduler.StartAsync()
|
|
started = true
|
|
lock.Unlock()
|
|
graceful.GetManager().RunAtShutdown(context.Background(), func() {
|
|
scheduler.Stop()
|
|
lock.Lock()
|
|
started = false
|
|
lock.Unlock()
|
|
finished()
|
|
})
|
|
}
|
|
|
|
// TaskTableRow represents a task row in the tasks table
|
|
type TaskTableRow struct {
|
|
Name string
|
|
Spec string
|
|
Next time.Time
|
|
Prev time.Time
|
|
Status string
|
|
LastMessage string
|
|
LastDoer string
|
|
ExecTimes int64
|
|
task *Task
|
|
}
|
|
|
|
func (t *TaskTableRow) FormatLastMessage(locale translation.Locale) string {
|
|
if t.Status == "finished" {
|
|
return t.task.GetConfig().FormatMessage(locale, t.Name, t.Status, t.LastDoer)
|
|
}
|
|
|
|
return t.task.GetConfig().FormatMessage(locale, t.Name, t.Status, t.LastDoer, t.LastMessage)
|
|
}
|
|
|
|
// TaskTable represents a table of tasks
|
|
type TaskTable []*TaskTableRow
|
|
|
|
// ListTasks returns all running cron tasks.
|
|
func ListTasks() TaskTable {
|
|
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 := "-"
|
|
var (
|
|
next time.Time
|
|
prev time.Time
|
|
)
|
|
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()
|
|
}
|
|
|
|
// If the manual run is after the cron run, use that instead.
|
|
if prev.Before(task.LastRun) {
|
|
prev = task.LastRun
|
|
}
|
|
|
|
task.lock.Lock()
|
|
tTable = append(tTable, &TaskTableRow{
|
|
Name: task.Name,
|
|
Spec: spec,
|
|
Next: next,
|
|
Prev: prev,
|
|
ExecTimes: task.ExecTimes,
|
|
LastMessage: task.LastMessage,
|
|
Status: task.Status,
|
|
LastDoer: task.LastDoer,
|
|
task: task,
|
|
})
|
|
task.lock.Unlock()
|
|
}
|
|
|
|
return tTable
|
|
}
|