Skip to content

Commit 76f2a0f

Browse files
earl-warrenGustedKN4CK3Rsilverwind
authored andcommitted
show manual cron run's last time (go-gitea#27544)
- 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 <[email protected]> Co-authored-by: KN4CK3R <[email protected]> Co-authored-by: silverwind <[email protected]>
1 parent f19feb0 commit 76f2a0f

File tree

3 files changed

+65
-0
lines changed

3 files changed

+65
-0
lines changed

services/cron/cron.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,12 @@ func ListTasks() TaskTable {
106106
next = e.NextRun()
107107
prev = e.PreviousRun()
108108
}
109+
110+
// If the manual run is after the cron run, use that instead.
111+
if prev.Before(task.LastRun) {
112+
prev = task.LastRun
113+
}
114+
109115
task.lock.Lock()
110116
tTable = append(tTable, &TaskTableRow{
111117
Name: task.Name,

services/cron/tasks.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"reflect"
1010
"strings"
1111
"sync"
12+
"time"
1213

1314
"code.gitea.io/gitea/models/db"
1415
system_model "code.gitea.io/gitea/models/system"
@@ -37,6 +38,8 @@ type Task struct {
3738
LastMessage string
3839
LastDoer string
3940
ExecTimes int64
41+
// This stores the time of the last manual run of this task.
42+
LastRun time.Time
4043
}
4144

4245
// DoRunAtStart returns if this task should run at the start
@@ -88,6 +91,12 @@ func (t *Task) RunWithUser(doer *user_model.User, config Config) {
8891
}
8992
}()
9093
graceful.GetManager().RunWithShutdownContext(func(baseCtx context.Context) {
94+
// Store the time of this run, before the function is executed, so it
95+
// matches the behavior of what the cron library does.
96+
t.lock.Lock()
97+
t.LastRun = time.Now()
98+
t.lock.Unlock()
99+
91100
pm := process.GetManager()
92101
doerName := ""
93102
if doer != nil && doer.ID != -1 {

tests/integration/api_admin_test.go

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"fmt"
88
"net/http"
99
"testing"
10+
"time"
1011

1112
asymkey_model "code.gitea.io/gitea/models/asymkey"
1213
auth_model "code.gitea.io/gitea/models/auth"
@@ -282,3 +283,52 @@ func TestAPIRenameUser(t *testing.T) {
282283
})
283284
MakeRequest(t, req, http.StatusOK)
284285
}
286+
287+
func TestAPICron(t *testing.T) {
288+
defer tests.PrepareTestEnv(t)()
289+
290+
// user1 is an admin user
291+
session := loginUser(t, "user1")
292+
293+
t.Run("List", func(t *testing.T) {
294+
defer tests.PrintCurrentTest(t)()
295+
296+
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadAdmin)
297+
urlStr := fmt.Sprintf("/api/v1/admin/cron?token=%s", token)
298+
req := NewRequest(t, "GET", urlStr)
299+
resp := MakeRequest(t, req, http.StatusOK)
300+
301+
assert.Equal(t, "28", resp.Header().Get("X-Total-Count"))
302+
303+
var crons []api.Cron
304+
DecodeJSON(t, resp, &crons)
305+
assert.Len(t, crons, 28)
306+
})
307+
308+
t.Run("Execute", func(t *testing.T) {
309+
defer tests.PrintCurrentTest(t)()
310+
311+
now := time.Now()
312+
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteAdmin)
313+
// Archive cleanup is harmless, because in the test environment there are none
314+
// and is thus an NOOP operation and therefore doesn't interfere with any other
315+
// tests.
316+
urlStr := fmt.Sprintf("/api/v1/admin/cron/archive_cleanup?token=%s", token)
317+
req := NewRequest(t, "POST", urlStr)
318+
MakeRequest(t, req, http.StatusNoContent)
319+
320+
// Check for the latest run time for this cron, to ensure it has been run.
321+
urlStr = fmt.Sprintf("/api/v1/admin/cron?token=%s", token)
322+
req = NewRequest(t, "GET", urlStr)
323+
resp := MakeRequest(t, req, http.StatusOK)
324+
325+
var crons []api.Cron
326+
DecodeJSON(t, resp, &crons)
327+
328+
for _, cron := range crons {
329+
if cron.Name == "archive_cleanup" {
330+
assert.True(t, now.Before(cron.Prev))
331+
}
332+
}
333+
})
334+
}

0 commit comments

Comments
 (0)