forked from forgejo/forgejo
Refactor Cron and merge dashboard tasks (#10745)
* Refactor Cron and merge dashboard tasks * Merge Cron and Dashboard tasks * Make every cron task report a system notice on completion * Refactor the creation of these tasks * Ensure that execution counts of tasks is correct * Allow cron tasks to be started from the cron page * golangci-lint fixes * Enforce that only one task with the same name can be registered Signed-off-by: Andrew Thornton <art27@cantab.net> * fix name check Signed-off-by: Andrew Thornton <art27@cantab.net> * as per @guillep2k * as per @lafriks Signed-off-by: Andrew Thornton <art27@cantab.net> * Add git.CommandContext variants Signed-off-by: Andrew Thornton <art27@cantab.net> Co-authored-by: Lauris BH <lauris@nix.lv> Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com> Co-authored-by: techknowlogick <techknowlogick@gitea.io>
This commit is contained in:
parent
c18144086f
commit
9a2e47b23a
25 changed files with 851 additions and 453 deletions
|
@ -51,7 +51,7 @@ func (f *AdminEditUserForm) Validate(ctx *macaron.Context, errs binding.Errors)
|
|||
|
||||
// AdminDashboardForm form for admin dashboard operations
|
||||
type AdminDashboardForm struct {
|
||||
Op int `binding:"required"`
|
||||
Op string `binding:"required"`
|
||||
}
|
||||
|
||||
// Validate validates form fields
|
||||
|
|
|
@ -9,143 +9,86 @@ import (
|
|||
"context"
|
||||
"time"
|
||||
|
||||
"code.gitea.io/gitea/models"
|
||||
"code.gitea.io/gitea/modules/graceful"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/migrations"
|
||||
repo_module "code.gitea.io/gitea/modules/repository"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/sync"
|
||||
mirror_service "code.gitea.io/gitea/services/mirror"
|
||||
|
||||
"github.com/gogs/cron"
|
||||
)
|
||||
|
||||
const (
|
||||
mirrorUpdate = "mirror_update"
|
||||
gitFsck = "git_fsck"
|
||||
checkRepos = "check_repos"
|
||||
archiveCleanup = "archive_cleanup"
|
||||
syncExternalUsers = "sync_external_users"
|
||||
deletedBranchesCleanup = "deleted_branches_cleanup"
|
||||
updateMigrationPosterID = "update_migration_post_id"
|
||||
)
|
||||
|
||||
var c = cron.New()
|
||||
|
||||
// Prevent duplicate running tasks.
|
||||
var taskStatusTable = sync.NewStatusTable()
|
||||
|
||||
// Func defines a cron function body
|
||||
type Func func()
|
||||
|
||||
// WithUnique wrap a cron func with an unique running check
|
||||
func WithUnique(name string, body func(context.Context)) Func {
|
||||
return func() {
|
||||
if !taskStatusTable.StartIfNotRunning(name) {
|
||||
return
|
||||
}
|
||||
defer taskStatusTable.Stop(name)
|
||||
graceful.GetManager().RunWithShutdownContext(body)
|
||||
}
|
||||
}
|
||||
|
||||
// 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() {
|
||||
var (
|
||||
entry *cron.Entry
|
||||
err error
|
||||
)
|
||||
if setting.Cron.UpdateMirror.Enabled {
|
||||
entry, err = c.AddFunc("Update mirrors", setting.Cron.UpdateMirror.Schedule, WithUnique(mirrorUpdate, mirror_service.Update))
|
||||
if err != nil {
|
||||
log.Fatal("Cron[Update mirrors]: %v", err)
|
||||
}
|
||||
if setting.Cron.UpdateMirror.RunAtStart {
|
||||
entry.Prev = time.Now()
|
||||
entry.ExecTimes++
|
||||
go WithUnique(mirrorUpdate, mirror_service.Update)()
|
||||
}
|
||||
}
|
||||
if setting.Cron.RepoHealthCheck.Enabled {
|
||||
entry, err = c.AddFunc("Repository health check", setting.Cron.RepoHealthCheck.Schedule, WithUnique(gitFsck, func(ctx context.Context) {
|
||||
if err := repo_module.GitFsck(ctx); err != nil {
|
||||
log.Error("GitFsck: %s", err)
|
||||
}
|
||||
}))
|
||||
if err != nil {
|
||||
log.Fatal("Cron[Repository health check]: %v", err)
|
||||
}
|
||||
if setting.Cron.RepoHealthCheck.RunAtStart {
|
||||
entry.Prev = time.Now()
|
||||
entry.ExecTimes++
|
||||
go WithUnique(gitFsck, func(ctx context.Context) {
|
||||
if err := repo_module.GitFsck(ctx); err != nil {
|
||||
log.Error("GitFsck: %s", err)
|
||||
}
|
||||
})()
|
||||
}
|
||||
}
|
||||
if setting.Cron.CheckRepoStats.Enabled {
|
||||
entry, err = c.AddFunc("Check repository statistics", setting.Cron.CheckRepoStats.Schedule, WithUnique(checkRepos, models.CheckRepoStats))
|
||||
if err != nil {
|
||||
log.Fatal("Cron[Check repository statistics]: %v", err)
|
||||
}
|
||||
if setting.Cron.CheckRepoStats.RunAtStart {
|
||||
entry.Prev = time.Now()
|
||||
entry.ExecTimes++
|
||||
go WithUnique(checkRepos, models.CheckRepoStats)()
|
||||
}
|
||||
}
|
||||
if setting.Cron.ArchiveCleanup.Enabled {
|
||||
entry, err = c.AddFunc("Clean up old repository archives", setting.Cron.ArchiveCleanup.Schedule, WithUnique(archiveCleanup, models.DeleteOldRepositoryArchives))
|
||||
if err != nil {
|
||||
log.Fatal("Cron[Clean up old repository archives]: %v", err)
|
||||
}
|
||||
if setting.Cron.ArchiveCleanup.RunAtStart {
|
||||
entry.Prev = time.Now()
|
||||
entry.ExecTimes++
|
||||
go WithUnique(archiveCleanup, models.DeleteOldRepositoryArchives)()
|
||||
}
|
||||
}
|
||||
if setting.Cron.SyncExternalUsers.Enabled {
|
||||
entry, err = c.AddFunc("Synchronize external users", setting.Cron.SyncExternalUsers.Schedule, WithUnique(syncExternalUsers, models.SyncExternalUsers))
|
||||
if err != nil {
|
||||
log.Fatal("Cron[Synchronize external users]: %v", err)
|
||||
}
|
||||
if setting.Cron.SyncExternalUsers.RunAtStart {
|
||||
entry.Prev = time.Now()
|
||||
entry.ExecTimes++
|
||||
go WithUnique(syncExternalUsers, models.SyncExternalUsers)()
|
||||
}
|
||||
}
|
||||
if setting.Cron.DeletedBranchesCleanup.Enabled {
|
||||
entry, err = c.AddFunc("Remove old deleted branches", setting.Cron.DeletedBranchesCleanup.Schedule, WithUnique(deletedBranchesCleanup, models.RemoveOldDeletedBranches))
|
||||
if err != nil {
|
||||
log.Fatal("Cron[Remove old deleted branches]: %v", err)
|
||||
}
|
||||
if setting.Cron.DeletedBranchesCleanup.RunAtStart {
|
||||
entry.Prev = time.Now()
|
||||
entry.ExecTimes++
|
||||
go WithUnique(deletedBranchesCleanup, models.RemoveOldDeletedBranches)()
|
||||
}
|
||||
}
|
||||
initBasicTasks()
|
||||
initExtendedTasks()
|
||||
|
||||
entry, err = c.AddFunc("Update migrated repositories' issues and comments' posterid", setting.Cron.UpdateMigrationPosterID.Schedule, WithUnique(updateMigrationPosterID, migrations.UpdateMigrationPosterID))
|
||||
if err != nil {
|
||||
log.Fatal("Cron[Update migrated repositories]: %v", err)
|
||||
lock.Lock()
|
||||
for _, task := range tasks {
|
||||
if task.IsEnabled() && task.DoRunAtStart() {
|
||||
go task.Run()
|
||||
}
|
||||
}
|
||||
entry.Prev = time.Now()
|
||||
entry.ExecTimes++
|
||||
go WithUnique(updateMigrationPosterID, migrations.UpdateMigrationPosterID)()
|
||||
|
||||
c.Start()
|
||||
graceful.GetManager().RunAtShutdown(context.Background(), c.Stop)
|
||||
started = true
|
||||
lock.Unlock()
|
||||
graceful.GetManager().RunAtShutdown(context.Background(), func() {
|
||||
c.Stop()
|
||||
lock.Lock()
|
||||
started = false
|
||||
lock.Unlock()
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
// ListTasks returns all running cron tasks.
|
||||
func ListTasks() []*cron.Entry {
|
||||
return c.Entries()
|
||||
// TaskTableRow represents a task row in the tasks table
|
||||
type TaskTableRow struct {
|
||||
Name string
|
||||
Spec string
|
||||
Next time.Time
|
||||
Prev time.Time
|
||||
ExecTimes int64
|
||||
}
|
||||
|
||||
// TaskTable represents a table of tasks
|
||||
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
|
||||
}
|
||||
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 := eMap[task.Name]; ok {
|
||||
spec = e.Spec
|
||||
next = e.Next
|
||||
prev = e.Prev
|
||||
}
|
||||
task.lock.Lock()
|
||||
tTable = append(tTable, &TaskTableRow{
|
||||
Name: task.Name,
|
||||
Spec: spec,
|
||||
Next: next,
|
||||
Prev: prev,
|
||||
ExecTimes: task.ExecTimes,
|
||||
})
|
||||
task.lock.Unlock()
|
||||
}
|
||||
|
||||
return tTable
|
||||
}
|
||||
|
|
72
modules/cron/setting.go
Normal file
72
modules/cron/setting.go
Normal file
|
@ -0,0 +1,72 @@
|
|||
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package cron
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"code.gitea.io/gitea/models"
|
||||
"github.com/unknwon/i18n"
|
||||
)
|
||||
|
||||
// Config represents a basic configuration interface that cron task
|
||||
type Config interface {
|
||||
IsEnabled() bool
|
||||
DoRunAtStart() bool
|
||||
GetSchedule() string
|
||||
FormatMessage(name, status string, doer *models.User, args ...interface{}) string
|
||||
}
|
||||
|
||||
// BaseConfig represents the basic config for a Cron task
|
||||
type BaseConfig struct {
|
||||
Enabled bool
|
||||
RunAtStart bool
|
||||
Schedule string
|
||||
}
|
||||
|
||||
// OlderThanConfig represents a cron task with OlderThan setting
|
||||
type OlderThanConfig struct {
|
||||
BaseConfig
|
||||
OlderThan time.Duration
|
||||
}
|
||||
|
||||
// UpdateExistingConfig represents a cron task with UpdateExisting setting
|
||||
type UpdateExistingConfig struct {
|
||||
BaseConfig
|
||||
UpdateExisting bool
|
||||
}
|
||||
|
||||
// GetSchedule returns the schedule for the base config
|
||||
func (b *BaseConfig) GetSchedule() string {
|
||||
return b.Schedule
|
||||
}
|
||||
|
||||
// IsEnabled returns the enabled status for the config
|
||||
func (b *BaseConfig) IsEnabled() bool {
|
||||
return b.Enabled
|
||||
}
|
||||
|
||||
// DoRunAtStart returns whether the task should be run at the start
|
||||
func (b *BaseConfig) DoRunAtStart() bool {
|
||||
return b.RunAtStart
|
||||
}
|
||||
|
||||
// FormatMessage returns a message for the task
|
||||
func (b *BaseConfig) FormatMessage(name, status string, doer *models.User, args ...interface{}) string {
|
||||
realArgs := make([]interface{}, 0, len(args)+2)
|
||||
realArgs = append(realArgs, i18n.Tr("en-US", "admin.dashboard."+name))
|
||||
if doer == nil {
|
||||
realArgs = append(realArgs, "(Cron)")
|
||||
} else {
|
||||
realArgs = append(realArgs, doer.Name)
|
||||
}
|
||||
if len(args) > 0 {
|
||||
realArgs = append(realArgs, args...)
|
||||
}
|
||||
if doer == nil || (doer.ID == -1 && doer.Name == "(Cron)") {
|
||||
return i18n.Tr("en-US", "admin.dashboard.cron."+status, realArgs...)
|
||||
}
|
||||
return i18n.Tr("en-US", "admin.dashboard.task."+status, realArgs...)
|
||||
}
|
166
modules/cron/tasks.go
Normal file
166
modules/cron/tasks.go
Normal file
|
@ -0,0 +1,166 @@
|
|||
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package cron
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"sync"
|
||||
|
||||
"code.gitea.io/gitea/models"
|
||||
"code.gitea.io/gitea/modules/graceful"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/process"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
)
|
||||
|
||||
var lock = sync.Mutex{}
|
||||
var started = false
|
||||
var tasks = []*Task{}
|
||||
var tasksMap = map[string]*Task{}
|
||||
|
||||
// Task represents a Cron task
|
||||
type Task struct {
|
||||
lock sync.Mutex
|
||||
Name string
|
||||
config Config
|
||||
fun func(context.Context, *models.User, Config) error
|
||||
ExecTimes int64
|
||||
}
|
||||
|
||||
// DoRunAtStart returns if this task should run at the start
|
||||
func (t *Task) DoRunAtStart() bool {
|
||||
return t.config.DoRunAtStart()
|
||||
}
|
||||
|
||||
// IsEnabled returns if this task is enabled as cron task
|
||||
func (t *Task) IsEnabled() bool {
|
||||
return t.config.IsEnabled()
|
||||
}
|
||||
|
||||
// GetConfig will return a copy of the task's config
|
||||
func (t *Task) GetConfig() Config {
|
||||
if reflect.TypeOf(t.config).Kind() == reflect.Ptr {
|
||||
// Pointer:
|
||||
return reflect.New(reflect.ValueOf(t.config).Elem().Type()).Interface().(Config)
|
||||
}
|
||||
// Not pointer:
|
||||
return reflect.New(reflect.TypeOf(t.config)).Elem().Interface().(Config)
|
||||
}
|
||||
|
||||
// Run will run the task incrementing the cron counter with no user defined
|
||||
func (t *Task) Run() {
|
||||
t.RunWithUser(&models.User{
|
||||
ID: -1,
|
||||
Name: "(Cron)",
|
||||
LowerName: "(cron)",
|
||||
}, t.config)
|
||||
}
|
||||
|
||||
// RunWithUser will run the task incrementing the cron counter at the time with User
|
||||
func (t *Task) RunWithUser(doer *models.User, config Config) {
|
||||
if !taskStatusTable.StartIfNotRunning(t.Name) {
|
||||
return
|
||||
}
|
||||
t.lock.Lock()
|
||||
if config == nil {
|
||||
config = t.config
|
||||
}
|
||||
t.ExecTimes++
|
||||
t.lock.Unlock()
|
||||
defer func() {
|
||||
taskStatusTable.Stop(t.Name)
|
||||
if err := recover(); err != nil {
|
||||
// Recover a panic within the
|
||||
combinedErr := fmt.Errorf("%s\n%s", err, log.Stack(2))
|
||||
log.Error("PANIC whilst running task: %s Value: %v", t.Name, combinedErr)
|
||||
}
|
||||
}()
|
||||
graceful.GetManager().RunWithShutdownContext(func(baseCtx context.Context) {
|
||||
ctx, cancel := context.WithCancel(baseCtx)
|
||||
defer cancel()
|
||||
pm := process.GetManager()
|
||||
pid := pm.Add(config.FormatMessage(t.Name, "process", doer), cancel)
|
||||
defer pm.Remove(pid)
|
||||
if err := t.fun(ctx, doer, config); err != nil {
|
||||
if models.IsErrCancelled(err) {
|
||||
message := err.(models.ErrCancelled).Message
|
||||
if err := models.CreateNotice(models.NoticeTask, config.FormatMessage(t.Name, "aborted", doer, message)); err != nil {
|
||||
log.Error("CreateNotice: %v", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
if err := models.CreateNotice(models.NoticeTask, config.FormatMessage(t.Name, "error", doer, err)); err != nil {
|
||||
log.Error("CreateNotice: %v", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
if err := models.CreateNotice(models.NoticeTask, config.FormatMessage(t.Name, "finished", doer)); err != nil {
|
||||
log.Error("CreateNotice: %v", err)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// GetTask gets the named task
|
||||
func GetTask(name string) *Task {
|
||||
lock.Lock()
|
||||
defer lock.Unlock()
|
||||
log.Info("Getting %s in %v", name, tasksMap[name])
|
||||
|
||||
return tasksMap[name]
|
||||
}
|
||||
|
||||
// RegisterTask allows a task to be registered with the cron service
|
||||
func RegisterTask(name string, config Config, fun func(context.Context, *models.User, Config) error) error {
|
||||
log.Debug("Registering task: %s", name)
|
||||
_, err := setting.GetCronSettings(name, config)
|
||||
if err != nil {
|
||||
log.Error("Unable to register cron task with name: %s Error: %v", name, err)
|
||||
return err
|
||||
}
|
||||
|
||||
task := &Task{
|
||||
Name: name,
|
||||
config: config,
|
||||
fun: fun,
|
||||
}
|
||||
lock.Lock()
|
||||
locked := true
|
||||
defer func() {
|
||||
if locked {
|
||||
lock.Unlock()
|
||||
}
|
||||
}()
|
||||
if _, has := tasksMap[task.Name]; has {
|
||||
log.Error("A task with this name: %s has already been registered", name)
|
||||
return fmt.Errorf("duplicate task with name: %s", task.Name)
|
||||
}
|
||||
|
||||
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)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
tasks = append(tasks, task)
|
||||
tasksMap[task.Name] = task
|
||||
if started && config.IsEnabled() && config.DoRunAtStart() {
|
||||
lock.Unlock()
|
||||
locked = false
|
||||
task.Run()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// RegisterTaskFatal will register a task but if there is an error log.Fatal
|
||||
func RegisterTaskFatal(name string, config Config, fun func(context.Context, *models.User, Config) error) {
|
||||
if err := RegisterTask(name, config, fun); err != nil {
|
||||
log.Fatal("Unable to register cron task %s Error: %v", name, err)
|
||||
}
|
||||
}
|
118
modules/cron/tasks_basic.go
Normal file
118
modules/cron/tasks_basic.go
Normal file
|
@ -0,0 +1,118 @@
|
|||
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package cron
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"code.gitea.io/gitea/models"
|
||||
"code.gitea.io/gitea/modules/migrations"
|
||||
repository_service "code.gitea.io/gitea/modules/repository"
|
||||
mirror_service "code.gitea.io/gitea/services/mirror"
|
||||
)
|
||||
|
||||
func registerUpdateMirrorTask() {
|
||||
RegisterTaskFatal("update_mirrors", &BaseConfig{
|
||||
Enabled: true,
|
||||
RunAtStart: false,
|
||||
Schedule: "@every 10m",
|
||||
}, func(ctx context.Context, _ *models.User, _ Config) error {
|
||||
return mirror_service.Update(ctx)
|
||||
})
|
||||
}
|
||||
|
||||
func registerRepoHealthCheck() {
|
||||
type RepoHealthCheckConfig struct {
|
||||
BaseConfig
|
||||
Timeout time.Duration
|
||||
Args []string `delim:" "`
|
||||
}
|
||||
RegisterTaskFatal("repo_health_check", &RepoHealthCheckConfig{
|
||||
BaseConfig: BaseConfig{
|
||||
Enabled: true,
|
||||
RunAtStart: false,
|
||||
Schedule: "@every 24h",
|
||||
},
|
||||
Timeout: 60 * time.Second,
|
||||
Args: []string{},
|
||||
}, func(ctx context.Context, _ *models.User, config Config) error {
|
||||
rhcConfig := config.(*RepoHealthCheckConfig)
|
||||
return repository_service.GitFsck(ctx, rhcConfig.Timeout, rhcConfig.Args)
|
||||
})
|
||||
}
|
||||
|
||||
func registerCheckRepoStats() {
|
||||
RegisterTaskFatal("check_repo_stats", &BaseConfig{
|
||||
Enabled: true,
|
||||
RunAtStart: true,
|
||||
Schedule: "@every 24h",
|
||||
}, func(ctx context.Context, _ *models.User, _ Config) error {
|
||||
return models.CheckRepoStats(ctx)
|
||||
})
|
||||
}
|
||||
|
||||
func registerArchiveCleanup() {
|
||||
RegisterTaskFatal("archive_cleanup", &OlderThanConfig{
|
||||
BaseConfig: BaseConfig{
|
||||
Enabled: true,
|
||||
RunAtStart: true,
|
||||
Schedule: "@every 24h",
|
||||
},
|
||||
OlderThan: 24 * time.Hour,
|
||||
}, func(ctx context.Context, _ *models.User, config Config) error {
|
||||
acConfig := config.(*OlderThanConfig)
|
||||
return models.DeleteOldRepositoryArchives(ctx, acConfig.OlderThan)
|
||||
})
|
||||
}
|
||||
|
||||
func registerSyncExternalUsers() {
|
||||
RegisterTaskFatal("sync_external_users", &UpdateExistingConfig{
|
||||
BaseConfig: BaseConfig{
|
||||
Enabled: true,
|
||||
RunAtStart: false,
|
||||
Schedule: "@every 24h",
|
||||
},
|
||||
UpdateExisting: true,
|
||||
}, func(ctx context.Context, _ *models.User, config Config) error {
|
||||
realConfig := config.(*UpdateExistingConfig)
|
||||
return models.SyncExternalUsers(ctx, realConfig.UpdateExisting)
|
||||
})
|
||||
}
|
||||
|
||||
func registerDeletedBranchesCleanup() {
|
||||
RegisterTaskFatal("deleted_branches_cleanup", &OlderThanConfig{
|
||||
BaseConfig: BaseConfig{
|
||||
Enabled: true,
|
||||
RunAtStart: true,
|
||||
Schedule: "@every 24h",
|
||||
},
|
||||
OlderThan: 24 * time.Hour,
|
||||
}, func(ctx context.Context, _ *models.User, config Config) error {
|
||||
realConfig := config.(*OlderThanConfig)
|
||||
models.RemoveOldDeletedBranches(ctx, realConfig.OlderThan)
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func registerUpdateMigrationPosterID() {
|
||||
RegisterTaskFatal("update_migration_poster_id", &BaseConfig{
|
||||
Enabled: true,
|
||||
RunAtStart: true,
|
||||
Schedule: "@every 24h",
|
||||
}, func(ctx context.Context, _ *models.User, _ Config) error {
|
||||
return migrations.UpdateMigrationPosterID(ctx)
|
||||
})
|
||||
}
|
||||
|
||||
func initBasicTasks() {
|
||||
registerUpdateMirrorTask()
|
||||
registerRepoHealthCheck()
|
||||
registerCheckRepoStats()
|
||||
registerArchiveCleanup()
|
||||
registerSyncExternalUsers()
|
||||
registerDeletedBranchesCleanup()
|
||||
registerUpdateMigrationPosterID()
|
||||
}
|
119
modules/cron/tasks_extended.go
Normal file
119
modules/cron/tasks_extended.go
Normal file
|
@ -0,0 +1,119 @@
|
|||
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package cron
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"code.gitea.io/gitea/models"
|
||||
repo_module "code.gitea.io/gitea/modules/repository"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
)
|
||||
|
||||
func registerDeleteInactiveUsers() {
|
||||
RegisterTaskFatal("delete_inactive_accounts", &OlderThanConfig{
|
||||
BaseConfig: BaseConfig{
|
||||
Enabled: false,
|
||||
RunAtStart: false,
|
||||
Schedule: "@annually",
|
||||
},
|
||||
OlderThan: 0 * time.Second,
|
||||
}, func(ctx context.Context, _ *models.User, config Config) error {
|
||||
olderThanConfig := config.(*OlderThanConfig)
|
||||
return models.DeleteInactiveUsers(ctx, olderThanConfig.OlderThan)
|
||||
})
|
||||
}
|
||||
|
||||
func registerDeleteRepositoryArchives() {
|
||||
RegisterTaskFatal("delete_repo_archives", &BaseConfig{
|
||||
Enabled: false,
|
||||
RunAtStart: false,
|
||||
Schedule: "@annually",
|
||||
}, func(ctx context.Context, _ *models.User, _ Config) error {
|
||||
return models.DeleteRepositoryArchives(ctx)
|
||||
})
|
||||
}
|
||||
|
||||
func registerGarbageCollectRepositories() {
|
||||
type RepoHealthCheckConfig struct {
|
||||
BaseConfig
|
||||
Timeout time.Duration
|
||||
Args []string `delim:" "`
|
||||
}
|
||||
RegisterTaskFatal("git_gc_repos", &RepoHealthCheckConfig{
|
||||
BaseConfig: BaseConfig{
|
||||
Enabled: false,
|
||||
RunAtStart: false,
|
||||
Schedule: "@every 72h",
|
||||
},
|
||||
Timeout: time.Duration(setting.Git.Timeout.GC) * time.Second,
|
||||
Args: setting.Git.GCArgs,
|
||||
}, func(ctx context.Context, _ *models.User, config Config) error {
|
||||
rhcConfig := config.(*RepoHealthCheckConfig)
|
||||
return repo_module.GitGcRepos(ctx, rhcConfig.Timeout, rhcConfig.Args...)
|
||||
})
|
||||
}
|
||||
|
||||
func registerRewriteAllPublicKeys() {
|
||||
RegisterTaskFatal("resync_all_sshkeys", &BaseConfig{
|
||||
Enabled: false,
|
||||
RunAtStart: false,
|
||||
Schedule: "@every 72h",
|
||||
}, func(_ context.Context, _ *models.User, _ Config) error {
|
||||
return models.RewriteAllPublicKeys()
|
||||
})
|
||||
}
|
||||
|
||||
func registerRepositoryUpdateHook() {
|
||||
RegisterTaskFatal("resync_all_hooks", &BaseConfig{
|
||||
Enabled: false,
|
||||
RunAtStart: false,
|
||||
Schedule: "@every 72h",
|
||||
}, func(ctx context.Context, _ *models.User, _ Config) error {
|
||||
return repo_module.SyncRepositoryHooks(ctx)
|
||||
})
|
||||
}
|
||||
|
||||
func registerReinitMissingRepositories() {
|
||||
RegisterTaskFatal("reinit_missing_repos", &BaseConfig{
|
||||
Enabled: false,
|
||||
RunAtStart: false,
|
||||
Schedule: "@every 72h",
|
||||
}, func(ctx context.Context, _ *models.User, _ Config) error {
|
||||
return repo_module.ReinitMissingRepositories(ctx)
|
||||
})
|
||||
}
|
||||
|
||||
func registerDeleteMissingRepositories() {
|
||||
RegisterTaskFatal("delete_missing_repos", &BaseConfig{
|
||||
Enabled: false,
|
||||
RunAtStart: false,
|
||||
Schedule: "@every 72h",
|
||||
}, func(ctx context.Context, user *models.User, _ Config) error {
|
||||
return repo_module.DeleteMissingRepositories(ctx, user)
|
||||
})
|
||||
}
|
||||
|
||||
func registerRemoveRandomAvatars() {
|
||||
RegisterTaskFatal("delete_generated_repository_avatars", &BaseConfig{
|
||||
Enabled: false,
|
||||
RunAtStart: false,
|
||||
Schedule: "@every 72h",
|
||||
}, func(ctx context.Context, _ *models.User, _ Config) error {
|
||||
return models.RemoveRandomAvatars(ctx)
|
||||
})
|
||||
}
|
||||
|
||||
func initExtendedTasks() {
|
||||
registerDeleteInactiveUsers()
|
||||
registerDeleteRepositoryArchives()
|
||||
registerGarbageCollectRepositories()
|
||||
registerRewriteAllPublicKeys()
|
||||
registerRepositoryUpdateHook()
|
||||
registerReinitMissingRepositories()
|
||||
registerDeleteMissingRepositories()
|
||||
registerRemoveRandomAvatars()
|
||||
}
|
|
@ -45,22 +45,32 @@ func (c *Command) String() string {
|
|||
|
||||
// NewCommand creates and returns a new Git Command based on given command and arguments.
|
||||
func NewCommand(args ...string) *Command {
|
||||
return NewCommandContext(DefaultContext, args...)
|
||||
}
|
||||
|
||||
// NewCommandContext creates and returns a new Git Command based on given command and arguments.
|
||||
func NewCommandContext(ctx context.Context, args ...string) *Command {
|
||||
// Make an explicit copy of GlobalCommandArgs, otherwise append might overwrite it
|
||||
cargs := make([]string, len(GlobalCommandArgs))
|
||||
copy(cargs, GlobalCommandArgs)
|
||||
return &Command{
|
||||
name: GitExecutable,
|
||||
args: append(cargs, args...),
|
||||
parentContext: DefaultContext,
|
||||
parentContext: ctx,
|
||||
}
|
||||
}
|
||||
|
||||
// NewCommandNoGlobals creates and returns a new Git Command based on given command and arguments only with the specify args and don't care global command args
|
||||
func NewCommandNoGlobals(args ...string) *Command {
|
||||
return NewCommandContextNoGlobals(DefaultContext, args...)
|
||||
}
|
||||
|
||||
// NewCommandContextNoGlobals creates and returns a new Git Command based on given command and arguments only with the specify args and don't care global command args
|
||||
func NewCommandContextNoGlobals(ctx context.Context, args ...string) *Command {
|
||||
return &Command{
|
||||
name: GitExecutable,
|
||||
args: args,
|
||||
parentContext: DefaultContext,
|
||||
parentContext: ctx,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -150,11 +150,11 @@ func Init(ctx context.Context) error {
|
|||
}
|
||||
|
||||
// Fsck verifies the connectivity and validity of the objects in the database
|
||||
func Fsck(repoPath string, timeout time.Duration, args ...string) error {
|
||||
func Fsck(ctx context.Context, repoPath string, timeout time.Duration, args ...string) error {
|
||||
// Make sure timeout makes sense.
|
||||
if timeout <= 0 {
|
||||
timeout = -1
|
||||
}
|
||||
_, err := NewCommand("fsck").AddArguments(args...).RunInDirTimeout(timeout, repoPath)
|
||||
_, err := NewCommandContext(ctx, "fsck").AddArguments(args...).RunInDirTimeout(timeout, repoPath)
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -13,17 +13,19 @@ import (
|
|||
)
|
||||
|
||||
// UpdateMigrationPosterID updates all migrated repositories' issues and comments posterID
|
||||
func UpdateMigrationPosterID(ctx context.Context) {
|
||||
func UpdateMigrationPosterID(ctx context.Context) error {
|
||||
for _, gitService := range structs.SupportedFullGitService {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
log.Warn("UpdateMigrationPosterID aborted due to shutdown before %s", gitService.Name())
|
||||
log.Warn("UpdateMigrationPosterID aborted before %s", gitService.Name())
|
||||
return models.ErrCancelledf("during UpdateMigrationPosterID before %s", gitService.Name())
|
||||
default:
|
||||
}
|
||||
if err := updateMigrationPosterIDByGitService(ctx, gitService); err != nil {
|
||||
log.Error("updateMigrationPosterIDByGitService failed: %v", err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func updateMigrationPosterIDByGitService(ctx context.Context, tp structs.GitServiceType) error {
|
||||
|
@ -37,7 +39,7 @@ func updateMigrationPosterIDByGitService(ctx context.Context, tp structs.GitServ
|
|||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
log.Warn("UpdateMigrationPosterIDByGitService(%s) aborted due to shutdown", tp.Name())
|
||||
log.Warn("UpdateMigrationPosterIDByGitService(%s) cancelled", tp.Name())
|
||||
return nil
|
||||
default:
|
||||
}
|
||||
|
@ -54,7 +56,7 @@ func updateMigrationPosterIDByGitService(ctx context.Context, tp structs.GitServ
|
|||
for _, user := range users {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
log.Warn("UpdateMigrationPosterIDByGitService(%s) aborted due to shutdown", tp.Name())
|
||||
log.Warn("UpdateMigrationPosterIDByGitService(%s) cancelled", tp.Name())
|
||||
return nil
|
||||
default:
|
||||
}
|
||||
|
|
|
@ -7,19 +7,19 @@ package repository
|
|||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"code.gitea.io/gitea/models"
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
|
||||
"github.com/unknwon/com"
|
||||
"xorm.io/builder"
|
||||
)
|
||||
|
||||
// GitFsck calls 'git fsck' to check repository health.
|
||||
func GitFsck(ctx context.Context) error {
|
||||
func GitFsck(ctx context.Context, timeout time.Duration, args []string) error {
|
||||
log.Trace("Doing: GitFsck")
|
||||
|
||||
if err := models.Iterate(
|
||||
|
@ -27,24 +27,24 @@ func GitFsck(ctx context.Context) error {
|
|||
new(models.Repository),
|
||||
builder.Expr("id>0 AND is_fsck_enabled=?", true),
|
||||
func(idx int, bean interface{}) error {
|
||||
repo := bean.(*models.Repository)
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return fmt.Errorf("Aborted due to shutdown")
|
||||
return models.ErrCancelledf("before fsck of %s", repo.FullName())
|
||||
default:
|
||||
}
|
||||
repo := bean.(*models.Repository)
|
||||
log.Trace("Running health check on repository %v", repo)
|
||||
repoPath := repo.RepoPath()
|
||||
log.Trace("Running health check on repository %s", repoPath)
|
||||
if err := git.Fsck(repoPath, setting.Cron.RepoHealthCheck.Timeout, setting.Cron.RepoHealthCheck.Args...); err != nil {
|
||||
desc := fmt.Sprintf("Failed to health check repository (%s): %v", repoPath, err)
|
||||
log.Warn(desc)
|
||||
if err = models.CreateRepositoryNotice(desc); err != nil {
|
||||
if err := git.Fsck(ctx, repoPath, timeout, args...); err != nil {
|
||||
log.Warn("Failed to health check repository (%v): %v", repo, err)
|
||||
if err = models.CreateRepositoryNotice("Failed to health check repository (%s): %v", repo.FullName(), err); err != nil {
|
||||
log.Error("CreateRepositoryNotice: %v", err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
},
|
||||
); err != nil {
|
||||
log.Trace("Error: GitFsck: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -53,32 +53,43 @@ func GitFsck(ctx context.Context) error {
|
|||
}
|
||||
|
||||
// GitGcRepos calls 'git gc' to remove unnecessary files and optimize the local repository
|
||||
func GitGcRepos(ctx context.Context) error {
|
||||
func GitGcRepos(ctx context.Context, timeout time.Duration, args ...string) error {
|
||||
log.Trace("Doing: GitGcRepos")
|
||||
args := append([]string{"gc"}, setting.Git.GCArgs...)
|
||||
args = append([]string{"gc"}, args...)
|
||||
|
||||
if err := models.Iterate(
|
||||
models.DefaultDBContext(),
|
||||
new(models.Repository),
|
||||
builder.Gt{"id": 0},
|
||||
func(idx int, bean interface{}) error {
|
||||
repo := bean.(*models.Repository)
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return fmt.Errorf("Aborted due to shutdown")
|
||||
return models.ErrCancelledf("before GC of %s", repo.FullName())
|
||||
default:
|
||||
}
|
||||
|
||||
repo := bean.(*models.Repository)
|
||||
if err := repo.GetOwner(); err != nil {
|
||||
return err
|
||||
log.Trace("Running git gc on %v", repo)
|
||||
command := git.NewCommandContext(ctx, args...).
|
||||
SetDescription(fmt.Sprintf("Repository Garbage Collection: %s", repo.FullName()))
|
||||
var stdout string
|
||||
var err error
|
||||
if timeout > 0 {
|
||||
var stdoutBytes []byte
|
||||
stdoutBytes, err = command.RunInDirTimeout(
|
||||
timeout,
|
||||
repo.RepoPath())
|
||||
stdout = string(stdoutBytes)
|
||||
} else {
|
||||
stdout, err = command.RunInDir(repo.RepoPath())
|
||||
}
|
||||
if stdout, err := git.NewCommand(args...).
|
||||
SetDescription(fmt.Sprintf("Repository Garbage Collection: %s", repo.FullName())).
|
||||
RunInDirTimeout(
|
||||
time.Duration(setting.Git.Timeout.GC)*time.Second,
|
||||
repo.RepoPath()); err != nil {
|
||||
|
||||
if err != nil {
|
||||
log.Error("Repository garbage collection failed for %v. Stdout: %s\nError: %v", repo, stdout, err)
|
||||
return fmt.Errorf("Repository garbage collection failed: Error: %v", err)
|
||||
desc := fmt.Sprintf("Repository garbage collection failed for %s. Stdout: %s\nError: %v", repo.RepoPath(), stdout, err)
|
||||
if err = models.CreateRepositoryNotice(desc); err != nil {
|
||||
log.Error("CreateRepositoryNotice: %v", err)
|
||||
}
|
||||
return fmt.Errorf("Repository garbage collection failed in repo: %s: Error: %v", repo.FullName(), err)
|
||||
}
|
||||
return nil
|
||||
},
|
||||
|
@ -90,7 +101,7 @@ func GitGcRepos(ctx context.Context) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func gatherMissingRepoRecords() ([]*models.Repository, error) {
|
||||
func gatherMissingRepoRecords(ctx context.Context) ([]*models.Repository, error) {
|
||||
repos := make([]*models.Repository, 0, 10)
|
||||
if err := models.Iterate(
|
||||
models.DefaultDBContext(),
|
||||
|
@ -98,24 +109,33 @@ func gatherMissingRepoRecords() ([]*models.Repository, error) {
|
|||
builder.Gt{"id": 0},
|
||||
func(idx int, bean interface{}) error {
|
||||
repo := bean.(*models.Repository)
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return models.ErrCancelledf("during gathering missing repo records before checking %s", repo.FullName())
|
||||
default:
|
||||
}
|
||||
if !com.IsDir(repo.RepoPath()) {
|
||||
repos = append(repos, repo)
|
||||
}
|
||||
return nil
|
||||
},
|
||||
); err != nil {
|
||||
if err2 := models.CreateRepositoryNotice(fmt.Sprintf("gatherMissingRepoRecords: %v", err)); err2 != nil {
|
||||
return nil, fmt.Errorf("CreateRepositoryNotice: %v", err)
|
||||
if strings.HasPrefix("Aborted gathering missing repo", err.Error()) {
|
||||
return nil, err
|
||||
}
|
||||
if err2 := models.CreateRepositoryNotice("gatherMissingRepoRecords: %v", err); err2 != nil {
|
||||
log.Error("CreateRepositoryNotice: %v", err2)
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
return repos, nil
|
||||
}
|
||||
|
||||
// DeleteMissingRepositories deletes all repository records that lost Git files.
|
||||
func DeleteMissingRepositories(doer *models.User) error {
|
||||
repos, err := gatherMissingRepoRecords()
|
||||
func DeleteMissingRepositories(ctx context.Context, doer *models.User) error {
|
||||
repos, err := gatherMissingRepoRecords(ctx)
|
||||
if err != nil {
|
||||
return fmt.Errorf("gatherMissingRepoRecords: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
if len(repos) == 0 {
|
||||
|
@ -123,10 +143,16 @@ func DeleteMissingRepositories(doer *models.User) error {
|
|||
}
|
||||
|
||||
for _, repo := range repos {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return models.ErrCancelledf("during DeleteMissingRepositories before %s", repo.FullName())
|
||||
default:
|
||||
}
|
||||
log.Trace("Deleting %d/%d...", repo.OwnerID, repo.ID)
|
||||
if err := models.DeleteRepository(doer, repo.OwnerID, repo.ID); err != nil {
|
||||
if err2 := models.CreateRepositoryNotice(fmt.Sprintf("DeleteRepository [%d]: %v", repo.ID, err)); err2 != nil {
|
||||
return fmt.Errorf("CreateRepositoryNotice: %v", err)
|
||||
log.Error("Failed to DeleteRepository %s [%d]: Error: %v", repo.FullName(), repo.ID, err)
|
||||
if err2 := models.CreateRepositoryNotice("Failed to DeleteRepository %s [%d]: Error: %v", repo.FullName(), repo.ID, err); err2 != nil {
|
||||
log.Error("CreateRepositoryNotice: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -134,10 +160,10 @@ func DeleteMissingRepositories(doer *models.User) error {
|
|||
}
|
||||
|
||||
// ReinitMissingRepositories reinitializes all repository records that lost Git files.
|
||||
func ReinitMissingRepositories() error {
|
||||
repos, err := gatherMissingRepoRecords()
|
||||
func ReinitMissingRepositories(ctx context.Context) error {
|
||||
repos, err := gatherMissingRepoRecords(ctx)
|
||||
if err != nil {
|
||||
return fmt.Errorf("gatherMissingRepoRecords: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
if len(repos) == 0 {
|
||||
|
@ -145,10 +171,16 @@ func ReinitMissingRepositories() error {
|
|||
}
|
||||
|
||||
for _, repo := range repos {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return models.ErrCancelledf("during ReinitMissingRepositories before %s", repo.FullName())
|
||||
default:
|
||||
}
|
||||
log.Trace("Initializing %d/%d...", repo.OwnerID, repo.ID)
|
||||
if err := git.InitRepository(repo.RepoPath(), true); err != nil {
|
||||
if err2 := models.CreateRepositoryNotice(fmt.Sprintf("InitRepository [%d]: %v", repo.ID, err)); err2 != nil {
|
||||
return fmt.Errorf("CreateRepositoryNotice: %v", err)
|
||||
log.Error("Unable (re)initialize repository %d at %s. Error: %v", repo.ID, repo.RepoPath(), err)
|
||||
if err2 := models.CreateRepositoryNotice("InitRepository [%d]: %v", repo.ID, err); err2 != nil {
|
||||
log.Error("CreateRepositoryNotice: %v", err2)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -160,17 +160,18 @@ func SyncRepositoryHooks(ctx context.Context) error {
|
|||
new(models.Repository),
|
||||
builder.Gt{"id": 0},
|
||||
func(idx int, bean interface{}) error {
|
||||
repo := bean.(*models.Repository)
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return fmt.Errorf("Aborted due to shutdown")
|
||||
return models.ErrCancelledf("before sync repository hooks for %s", repo.FullName())
|
||||
default:
|
||||
}
|
||||
|
||||
if err := createDelegateHooks(bean.(*models.Repository).RepoPath()); err != nil {
|
||||
if err := createDelegateHooks(repo.RepoPath()); err != nil {
|
||||
return fmt.Errorf("SyncRepositoryHook: %v", err)
|
||||
}
|
||||
if bean.(*models.Repository).HasWiki() {
|
||||
if err := createDelegateHooks(bean.(*models.Repository).WikiPath()); err != nil {
|
||||
if repo.HasWiki() {
|
||||
if err := createDelegateHooks(repo.WikiPath()); err != nil {
|
||||
return fmt.Errorf("SyncRepositoryHook: %v", err)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,129 +4,8 @@
|
|||
|
||||
package setting
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
)
|
||||
|
||||
var (
|
||||
|
||||
// Cron tasks
|
||||
Cron = struct {
|
||||
UpdateMirror struct {
|
||||
Enabled bool
|
||||
RunAtStart bool
|
||||
Schedule string
|
||||
} `ini:"cron.update_mirrors"`
|
||||
RepoHealthCheck struct {
|
||||
Enabled bool
|
||||
RunAtStart bool
|
||||
Schedule string
|
||||
Timeout time.Duration
|
||||
Args []string `delim:" "`
|
||||
} `ini:"cron.repo_health_check"`
|
||||
CheckRepoStats struct {
|
||||
Enabled bool
|
||||
RunAtStart bool
|
||||
Schedule string
|
||||
} `ini:"cron.check_repo_stats"`
|
||||
ArchiveCleanup struct {
|
||||
Enabled bool
|
||||
RunAtStart bool
|
||||
Schedule string
|
||||
OlderThan time.Duration
|
||||
} `ini:"cron.archive_cleanup"`
|
||||
SyncExternalUsers struct {
|
||||
Enabled bool
|
||||
RunAtStart bool
|
||||
Schedule string
|
||||
UpdateExisting bool
|
||||
} `ini:"cron.sync_external_users"`
|
||||
DeletedBranchesCleanup struct {
|
||||
Enabled bool
|
||||
RunAtStart bool
|
||||
Schedule string
|
||||
OlderThan time.Duration
|
||||
} `ini:"cron.deleted_branches_cleanup"`
|
||||
UpdateMigrationPosterID struct {
|
||||
Schedule string
|
||||
} `ini:"cron.update_migration_poster_id"`
|
||||
}{
|
||||
UpdateMirror: struct {
|
||||
Enabled bool
|
||||
RunAtStart bool
|
||||
Schedule string
|
||||
}{
|
||||
Enabled: true,
|
||||
RunAtStart: false,
|
||||
Schedule: "@every 10m",
|
||||
},
|
||||
RepoHealthCheck: struct {
|
||||
Enabled bool
|
||||
RunAtStart bool
|
||||
Schedule string
|
||||
Timeout time.Duration
|
||||
Args []string `delim:" "`
|
||||
}{
|
||||
Enabled: true,
|
||||
RunAtStart: false,
|
||||
Schedule: "@every 24h",
|
||||
Timeout: 60 * time.Second,
|
||||
Args: []string{},
|
||||
},
|
||||
CheckRepoStats: struct {
|
||||
Enabled bool
|
||||
RunAtStart bool
|
||||
Schedule string
|
||||
}{
|
||||
Enabled: true,
|
||||
RunAtStart: true,
|
||||
Schedule: "@every 24h",
|
||||
},
|
||||
ArchiveCleanup: struct {
|
||||
Enabled bool
|
||||
RunAtStart bool
|
||||
Schedule string
|
||||
OlderThan time.Duration
|
||||
}{
|
||||
Enabled: true,
|
||||
RunAtStart: true,
|
||||
Schedule: "@every 24h",
|
||||
OlderThan: 24 * time.Hour,
|
||||
},
|
||||
SyncExternalUsers: struct {
|
||||
Enabled bool
|
||||
RunAtStart bool
|
||||
Schedule string
|
||||
UpdateExisting bool
|
||||
}{
|
||||
Enabled: true,
|
||||
RunAtStart: false,
|
||||
Schedule: "@every 24h",
|
||||
UpdateExisting: true,
|
||||
},
|
||||
DeletedBranchesCleanup: struct {
|
||||
Enabled bool
|
||||
RunAtStart bool
|
||||
Schedule string
|
||||
OlderThan time.Duration
|
||||
}{
|
||||
Enabled: true,
|
||||
RunAtStart: true,
|
||||
Schedule: "@every 24h",
|
||||
OlderThan: 24 * time.Hour,
|
||||
},
|
||||
UpdateMigrationPosterID: struct {
|
||||
Schedule string
|
||||
}{
|
||||
Schedule: "@every 24h",
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
func newCron() {
|
||||
if err := Cfg.Section("cron").MapTo(&Cron); err != nil {
|
||||
log.Fatal("Failed to map Cron settings: %v", err)
|
||||
}
|
||||
// GetCronSettings maps the cron subsection to the provided config
|
||||
func GetCronSettings(name string, config interface{}) (interface{}, error) {
|
||||
err := Cfg.Section("cron." + name).MapTo(config)
|
||||
return config, err
|
||||
}
|
||||
|
|
|
@ -981,7 +981,6 @@ func NewContext() {
|
|||
u.Path = path.Join(u.Path, "api", "swagger")
|
||||
API.SwaggerURL = u.String()
|
||||
|
||||
newCron()
|
||||
newGit()
|
||||
|
||||
sec = Cfg.Section("mirror")
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue