forgejo/services/cron/cron.go
Gusted 2f66c1e4ce
[GITEA] Show manual cron run's last time
- 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 0475e2048e)
(cherry picked from commit dcc952f0db)
(cherry picked from commit 7168a240e8)
(cherry picked from commit 4bc4cccb1b)
(cherry picked from commit 3fe019ca3c)

[GITEA] Show manual cron run's last time (squash) 26 jobs in cron fixtures

(cherry picked from commit 8473030628)
(cherry picked from commit 871c729742)
(cherry picked from commit daefb27d2c)
2023-09-11 18:09:23 +02:00

131 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
}