chore: replace github.com/robfig/cron/v3 (#12365)

github.com/robfig/cron is used for parsing cron schedules of scheduled Forgejo Actions workflows. It has not seen an update in roughly six years and looks abandoned. There are multiple code paths that trigger panics instead of errors. It is replaced by github.com/gdgvda/cron, which is one of the few maintained forks. github.com/gdgvda/cron was picked because its behaviour is fully backwards-compatible and the developers are responsive.

Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/12365
Reviewed-by: limiting-factor <limiting-factor@noreply.codeberg.org>
Reviewed-by: Gusted <gusted@noreply.codeberg.org>
This commit is contained in:
Andreas Ahlenstorf 2026-05-01 22:07:22 +02:00 committed by Gusted
commit d867b25e72
5 changed files with 16 additions and 11 deletions

View file

@ -13,7 +13,7 @@ import (
"forgejo.org/modules/optional"
"forgejo.org/modules/timeutil"
"github.com/robfig/cron/v3"
"github.com/gdgvda/cron"
)
// ActionScheduleSpec represents a schedule spec of a workflow file
@ -53,16 +53,14 @@ func NewActionScheduleSpec(cron string, tz optional.Option[string], referenceTim
// Parse parses the spec and returns a cron.Schedule
// Unlike the default cron parser, Parse uses UTC timezone as the default if none is specified.
func (s *ActionScheduleSpec) Parse() (cron.Schedule, error) {
parser := cron.NewParser(cron.Minute | cron.Hour | cron.Dom | cron.Month | cron.Dow | cron.Descriptor)
schedule, err := parser.Parse(s.Spec)
parser, err := cron.NewDefaultParser(cron.Minute | cron.Hour | cron.Dom | cron.Month | cron.Dow | cron.Descriptor)
if err != nil {
return nil, err
}
specSchedule, ok := schedule.(*cron.SpecSchedule)
// If it's not a spec schedule, like "@every 5m", timezone is not relevant
if !ok {
return schedule, nil
schedule, err := parser.Parse(s.Spec)
if err != nil {
return nil, err
}
// If `timezone` is not defined in the workflow, but the spec includes a timezone, use it.
@ -81,8 +79,7 @@ func (s *ActionScheduleSpec) Parse() (cron.Schedule, error) {
location = time.UTC
}
specSchedule.Location = location
return specSchedule, nil
return schedule.WithLocation(location), nil
}
func init() {