Add per-remote sync_interval and spread GitHub syncs across it
ci/woodpecker/pr/pre-commit Pipeline was successful
ci/woodpecker/pr/test Pipeline was successful
ci/woodpecker/pr/build Pipeline was successful

This commit is contained in:
2026-10-25 13:12:59 +11:00
parent 9294686ea6
commit 707e380c98
14 changed files with 315 additions and 31 deletions
+31
View File
@@ -55,6 +55,9 @@ type Remote struct {
ImmutableTTL int `json:"immutable_ttl"`
MutableTTL int `json:"mutable_ttl"`
CheckMutable bool `json:"check_mutable"`
// SyncInterval (seconds) sets how often a github_* remote rescans releases
// and rebuilds its index; 0 falls back to mutable_ttl.
SyncInterval int `json:"sync_interval,omitempty"`
// Upstream HTTP timeouts in seconds. 0 means use the server default.
UpstreamDialTimeout int `json:"upstream_dial_timeout,omitempty"`
@@ -149,6 +152,34 @@ func (r *Remote) ValidateMirrorStrategy() error {
return nil
}
// SyncPeriod is the background sync cadence: sync_interval, else mutable_ttl,
// else def.
func (r Remote) SyncPeriod(def time.Duration) time.Duration {
if r.SyncInterval > 0 {
return time.Duration(r.SyncInterval) * time.Second
}
if r.MutableTTL > 0 {
return time.Duration(r.MutableTTL) * time.Second
}
return def
}
// ValidateSyncInterval rejects a negative sync_interval, or one set on a type
// without a background syncer.
func (r *Remote) ValidateSyncInterval() error {
if r.SyncInterval < 0 {
return fmt.Errorf("sync_interval must be >= 0, got %d", r.SyncInterval)
}
switch r.PackageType {
case PackageGitHubRPM, PackageGitHubDeb, PackageGitHubAlpine:
default:
if r.SyncInterval > 0 {
return fmt.Errorf("sync_interval is only valid on github_rpm, github_deb and github_alpine remotes")
}
}
return nil
}
// ValidatePatterns ensures every configured regex compiles. Storing an
// invalid pattern would otherwise be silently dropped at match time, which
// for the blocklist is a fail-open: a mistyped deny rule becomes a no-op.
+38
View File
@@ -4,6 +4,7 @@ import (
"encoding/json"
"strings"
"testing"
"time"
)
func TestRemote_ValidatePatterns(t *testing.T) {
@@ -123,3 +124,40 @@ func TestValidateMirrorlist(t *testing.T) {
})
}
}
func TestSyncPeriod(t *testing.T) {
const def = 5 * time.Minute
for _, tc := range []struct {
interval, ttl int
want time.Duration
}{
{0, 0, def},
{0, 600, 10 * time.Minute},
{3600, 600, time.Hour},
} {
r := Remote{SyncInterval: tc.interval, MutableTTL: tc.ttl}
if got := r.SyncPeriod(def); got != tc.want {
t.Errorf("SyncPeriod(interval=%d, ttl=%d) = %v, want %v", tc.interval, tc.ttl, got, tc.want)
}
}
}
func TestValidateSyncInterval(t *testing.T) {
for _, tc := range []struct {
pt PackageType
interval int
ok bool
}{
{PackageGitHubRPM, 3600, true},
{PackageGitHubDeb, 3600, true},
{PackageGitHubAlpine, 3600, true},
{PackageGitHubRPM, -1, false},
{PackageRPM, 0, true},
{PackageRPM, 60, false},
} {
r := Remote{PackageType: tc.pt, SyncInterval: tc.interval}
if err := r.ValidateSyncInterval(); (err == nil) != tc.ok {
t.Errorf("%s interval=%d: err=%v, want ok=%v", tc.pt, tc.interval, err, tc.ok)
}
}
}