Skip to content

Commit 90a48e9

Browse files
authored
Fix a bug where lfs gc never worked. (#35198)
Fix #31113 After #22385 introduced LFS GC, it never worked due to a bug in the INI library: fields in structs embedded more than one level deep are not populated from the INI file. This PR fixes the issue by replacing the multi-level embedded struct with a single-level struct for parsing the cron.gc_lfs configuration. Added a new test for retrieving cron settings to demonstrate the bug in the INI package.
1 parent 3450458 commit 90a48e9

File tree

3 files changed

+126
-21
lines changed

3 files changed

+126
-21
lines changed

modules/setting/cron_test.go

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,3 +41,56 @@ EXTEND = true
4141
assert.Equal(t, "white rabbit", extended.Second)
4242
assert.True(t, extended.Extend)
4343
}
44+
45+
// Test_getCronSettings2 tests that getCronSettings can not handle two levels of embedding
46+
func Test_getCronSettings2(t *testing.T) {
47+
type BaseStruct struct {
48+
Enabled bool
49+
RunAtStart bool
50+
Schedule string
51+
}
52+
53+
type Extended struct {
54+
BaseStruct
55+
Extend bool
56+
}
57+
type Extended2 struct {
58+
Extended
59+
Third string
60+
}
61+
62+
iniStr := `
63+
[cron.test]
64+
ENABLED = TRUE
65+
RUN_AT_START = TRUE
66+
SCHEDULE = @every 1h
67+
EXTEND = true
68+
THIRD = white rabbit
69+
`
70+
cfg, err := NewConfigProviderFromData(iniStr)
71+
assert.NoError(t, err)
72+
73+
extended := &Extended2{
74+
Extended: Extended{
75+
BaseStruct: BaseStruct{
76+
Enabled: false,
77+
RunAtStart: false,
78+
Schedule: "@every 72h",
79+
},
80+
Extend: false,
81+
},
82+
Third: "black rabbit",
83+
}
84+
85+
_, err = getCronSettings(cfg, "test", extended)
86+
assert.NoError(t, err)
87+
88+
// This confirms the first level of embedding works
89+
assert.Equal(t, "white rabbit", extended.Third)
90+
assert.True(t, extended.Extend)
91+
92+
// This confirms 2 levels of embedding doesn't work
93+
assert.False(t, extended.Enabled)
94+
assert.False(t, extended.RunAtStart)
95+
assert.Equal(t, "@every 72h", extended.Schedule)
96+
}

services/cron/tasks_extended.go

Lines changed: 22 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -171,34 +171,35 @@ func registerDeleteOldSystemNotices() {
171171
})
172172
}
173173

174+
type GCLFSConfig struct {
175+
BaseConfig
176+
OlderThan time.Duration
177+
LastUpdatedMoreThanAgo time.Duration
178+
NumberToCheckPerRepo int64
179+
ProportionToCheckPerRepo float64
180+
}
181+
174182
func registerGCLFS() {
175183
if !setting.LFS.StartServer {
176184
return
177185
}
178-
type GCLFSConfig struct {
179-
OlderThanConfig
180-
LastUpdatedMoreThanAgo time.Duration
181-
NumberToCheckPerRepo int64
182-
ProportionToCheckPerRepo float64
183-
}
184186

185187
RegisterTaskFatal("gc_lfs", &GCLFSConfig{
186-
OlderThanConfig: OlderThanConfig{
187-
BaseConfig: BaseConfig{
188-
Enabled: false,
189-
RunAtStart: false,
190-
Schedule: "@every 24h",
191-
},
192-
// Only attempt to garbage collect lfs meta objects older than a week as the order of git lfs upload
193-
// and git object upload is not necessarily guaranteed. It's possible to imagine a situation whereby
194-
// an LFS object is uploaded but the git branch is not uploaded immediately, or there are some rapid
195-
// changes in new branches that might lead to lfs objects becoming temporarily unassociated with git
196-
// objects.
197-
//
198-
// It is likely that a week is potentially excessive but it should definitely be enough that any
199-
// unassociated LFS object is genuinely unassociated.
200-
OlderThan: 24 * time.Hour * 7,
188+
BaseConfig: BaseConfig{
189+
Enabled: false,
190+
RunAtStart: false,
191+
Schedule: "@every 24h",
201192
},
193+
// Only attempt to garbage collect lfs meta objects older than a week as the order of git lfs upload
194+
// and git object upload is not necessarily guaranteed. It's possible to imagine a situation whereby
195+
// an LFS object is uploaded but the git branch is not uploaded immediately, or there are some rapid
196+
// changes in new branches that might lead to lfs objects becoming temporarily unassociated with git
197+
// objects.
198+
//
199+
// It is likely that a week is potentially excessive but it should definitely be enough that any
200+
// unassociated LFS object is genuinely unassociated.
201+
OlderThan: 24 * time.Hour * 7,
202+
202203
// Only GC things that haven't been looked at in the past 3 days
203204
LastUpdatedMoreThanAgo: 24 * time.Hour * 3,
204205
NumberToCheckPerRepo: 100,

services/cron/tasks_extended_test.go

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
// Copyright 2025 The Gitea Authors. All rights reserved.
2+
// SPDX-License-Identifier: MIT
3+
4+
package cron
5+
6+
import (
7+
"testing"
8+
"time"
9+
10+
"code.gitea.io/gitea/modules/setting"
11+
"code.gitea.io/gitea/modules/test"
12+
13+
"github.com/stretchr/testify/assert"
14+
)
15+
16+
func Test_GCLFSConfig(t *testing.T) {
17+
cfg, err := setting.NewConfigProviderFromData(`
18+
[cron.gc_lfs]
19+
ENABLED = true
20+
RUN_AT_START = true
21+
SCHEDULE = "@every 2h"
22+
OLDER_THAN = "1h"
23+
LAST_UPDATED_MORE_THAN_AGO = "7h"
24+
NUMBER_TO_CHECK_PER_REPO = 10
25+
PROPORTION_TO_CHECK_PER_REPO = 0.1
26+
`)
27+
assert.NoError(t, err)
28+
defer test.MockVariableValue(&setting.CfgProvider, cfg)()
29+
30+
config := &GCLFSConfig{
31+
BaseConfig: BaseConfig{
32+
Enabled: false,
33+
RunAtStart: false,
34+
Schedule: "@every 24h",
35+
},
36+
OlderThan: 24 * time.Hour * 7,
37+
LastUpdatedMoreThanAgo: 24 * time.Hour * 3,
38+
NumberToCheckPerRepo: 100,
39+
ProportionToCheckPerRepo: 0.6,
40+
}
41+
42+
_, err = setting.GetCronSettings("gc_lfs", config)
43+
assert.NoError(t, err)
44+
assert.True(t, config.Enabled)
45+
assert.True(t, config.RunAtStart)
46+
assert.Equal(t, "@every 2h", config.Schedule)
47+
assert.Equal(t, 1*time.Hour, config.OlderThan)
48+
assert.Equal(t, 7*time.Hour, config.LastUpdatedMoreThanAgo)
49+
assert.Equal(t, int64(10), config.NumberToCheckPerRepo)
50+
assert.InDelta(t, 0.1, config.ProportionToCheckPerRepo, 0.001)
51+
}

0 commit comments

Comments
 (0)