Skip to content

Commit 4c474e9

Browse files
committed
Add repounit fixer for go-gitea#16961 (go-gitea#17136)
Backport go-gitea#17136 Unfortunately a number of people appear to have been bitten by the bug in the dump command. This PR adds a doctor command to attempt to fix the broken repo_units Fix go-gitea#16961 Signed-off-by: Andrew Thornton <[email protected]>
1 parent 7ce938b commit 4c474e9

File tree

3 files changed

+276
-12
lines changed

3 files changed

+276
-12
lines changed

models/login_source.go

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -71,9 +71,9 @@ var (
7171
_ convert.Conversion = &SSPIConfig{}
7272
)
7373

74-
// jsonUnmarshalHandleDoubleEncode - due to a bug in xorm (see https://gitea.com/xorm/xorm/pulls/1957) - it's
74+
// JSONUnmarshalHandleDoubleEncode - due to a bug in xorm (see https://gitea.com/xorm/xorm/pulls/1957) - it's
7575
// possible that a Blob may be double encoded or gain an unwanted prefix of 0xff 0xfe.
76-
func jsonUnmarshalHandleDoubleEncode(bs []byte, v interface{}) error {
76+
func JSONUnmarshalHandleDoubleEncode(bs []byte, v interface{}) error {
7777
json := jsoniter.ConfigCompatibleWithStandardLibrary
7878
err := json.Unmarshal(bs, v)
7979
if err != nil {
@@ -108,7 +108,7 @@ type LDAPConfig struct {
108108

109109
// FromDB fills up a LDAPConfig from serialized format.
110110
func (cfg *LDAPConfig) FromDB(bs []byte) error {
111-
err := jsonUnmarshalHandleDoubleEncode(bs, &cfg)
111+
err := JSONUnmarshalHandleDoubleEncode(bs, &cfg)
112112
if err != nil {
113113
return err
114114
}
@@ -149,7 +149,7 @@ type SMTPConfig struct {
149149

150150
// FromDB fills up an SMTPConfig from serialized format.
151151
func (cfg *SMTPConfig) FromDB(bs []byte) error {
152-
return jsonUnmarshalHandleDoubleEncode(bs, cfg)
152+
return JSONUnmarshalHandleDoubleEncode(bs, cfg)
153153
}
154154

155155
// ToDB exports an SMTPConfig to a serialized format.
@@ -166,7 +166,7 @@ type PAMConfig struct {
166166

167167
// FromDB fills up a PAMConfig from serialized format.
168168
func (cfg *PAMConfig) FromDB(bs []byte) error {
169-
return jsonUnmarshalHandleDoubleEncode(bs, cfg)
169+
return JSONUnmarshalHandleDoubleEncode(bs, cfg)
170170
}
171171

172172
// ToDB exports a PAMConfig to a serialized format.
@@ -187,7 +187,7 @@ type OAuth2Config struct {
187187

188188
// FromDB fills up an OAuth2Config from serialized format.
189189
func (cfg *OAuth2Config) FromDB(bs []byte) error {
190-
return jsonUnmarshalHandleDoubleEncode(bs, cfg)
190+
return JSONUnmarshalHandleDoubleEncode(bs, cfg)
191191
}
192192

193193
// ToDB exports an SMTPConfig to a serialized format.
@@ -207,7 +207,7 @@ type SSPIConfig struct {
207207

208208
// FromDB fills up an SSPIConfig from serialized format.
209209
func (cfg *SSPIConfig) FromDB(bs []byte) error {
210-
return jsonUnmarshalHandleDoubleEncode(bs, cfg)
210+
return JSONUnmarshalHandleDoubleEncode(bs, cfg)
211211
}
212212

213213
// ToDB exports an SSPIConfig to a serialized format.

models/repo_unit.go

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ type UnitConfig struct{}
2828

2929
// FromDB fills up a UnitConfig from serialized format.
3030
func (cfg *UnitConfig) FromDB(bs []byte) error {
31-
return jsonUnmarshalHandleDoubleEncode(bs, &cfg)
31+
return JSONUnmarshalHandleDoubleEncode(bs, &cfg)
3232
}
3333

3434
// ToDB exports a UnitConfig to a serialized format.
@@ -44,7 +44,7 @@ type ExternalWikiConfig struct {
4444

4545
// FromDB fills up a ExternalWikiConfig from serialized format.
4646
func (cfg *ExternalWikiConfig) FromDB(bs []byte) error {
47-
return jsonUnmarshalHandleDoubleEncode(bs, &cfg)
47+
return JSONUnmarshalHandleDoubleEncode(bs, &cfg)
4848
}
4949

5050
// ToDB exports a ExternalWikiConfig to a serialized format.
@@ -62,7 +62,7 @@ type ExternalTrackerConfig struct {
6262

6363
// FromDB fills up a ExternalTrackerConfig from serialized format.
6464
func (cfg *ExternalTrackerConfig) FromDB(bs []byte) error {
65-
return jsonUnmarshalHandleDoubleEncode(bs, &cfg)
65+
return JSONUnmarshalHandleDoubleEncode(bs, &cfg)
6666
}
6767

6868
// ToDB exports a ExternalTrackerConfig to a serialized format.
@@ -80,7 +80,7 @@ type IssuesConfig struct {
8080

8181
// FromDB fills up a IssuesConfig from serialized format.
8282
func (cfg *IssuesConfig) FromDB(bs []byte) error {
83-
return jsonUnmarshalHandleDoubleEncode(bs, &cfg)
83+
return JSONUnmarshalHandleDoubleEncode(bs, &cfg)
8484
}
8585

8686
// ToDB exports a IssuesConfig to a serialized format.
@@ -104,7 +104,7 @@ type PullRequestsConfig struct {
104104

105105
// FromDB fills up a PullRequestsConfig from serialized format.
106106
func (cfg *PullRequestsConfig) FromDB(bs []byte) error {
107-
return jsonUnmarshalHandleDoubleEncode(bs, &cfg)
107+
return JSONUnmarshalHandleDoubleEncode(bs, &cfg)
108108
}
109109

110110
// ToDB exports a PullRequestsConfig to a serialized format.
@@ -219,3 +219,8 @@ func getUnitsByRepoID(e Engine, repoID int64) (units []*RepoUnit, err error) {
219219

220220
return units, nil
221221
}
222+
223+
func UpdateRepoUnit(unit *RepoUnit) error {
224+
_, err := x.ID(unit.ID).Update(unit)
225+
return err
226+
}

modules/doctor/fix16961.go

Lines changed: 259 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,259 @@
1+
// Copyright 2020 The Gitea Authors. All rights reserved.
2+
// Use of this source code is governed by a MIT-style
3+
// license that can be found in the LICENSE file.
4+
5+
package doctor
6+
7+
import (
8+
"bytes"
9+
"fmt"
10+
"strconv"
11+
12+
"code.gitea.io/gitea/models"
13+
"code.gitea.io/gitea/modules/log"
14+
"code.gitea.io/gitea/modules/timeutil"
15+
"xorm.io/builder"
16+
)
17+
18+
// #16831 revealed that the dump command that was broken in 1.14.3-1.14.6 and 1.15.0 (#15885).
19+
// This led to repo_unit and login_source cfg not being converted to JSON in the dump
20+
// Unfortunately although it was hoped that there were only a few users affected it
21+
// appears that many users are affected.
22+
23+
// We therefore need to provide a doctor command to fix this repeated issue #16961
24+
25+
func fixBrokenRepoUnits(logger log.Logger, autofix bool) error {
26+
// RepoUnit describes all units of a repository
27+
type RepoUnit struct {
28+
ID int64
29+
RepoID int64
30+
Type models.UnitType
31+
Config []byte
32+
CreatedUnix timeutil.TimeStamp `xorm:"INDEX CREATED"`
33+
}
34+
35+
count := 0
36+
37+
err := models.Iterate(
38+
models.DefaultDBContext(),
39+
new(RepoUnit),
40+
builder.Eq{"1": "1"},
41+
func(idx int, bean interface{}) error {
42+
unit := bean.(*RepoUnit)
43+
44+
bs := unit.Config
45+
repoUnit := &models.RepoUnit{
46+
ID: unit.ID,
47+
RepoID: unit.RepoID,
48+
Type: unit.Type,
49+
CreatedUnix: unit.CreatedUnix,
50+
}
51+
52+
switch models.UnitType(unit.Type) {
53+
case models.UnitTypeCode, models.UnitTypeReleases, models.UnitTypeWiki, models.UnitTypeProjects:
54+
cfg := &models.UnitConfig{}
55+
repoUnit.Config = cfg
56+
57+
err := models.JSONUnmarshalHandleDoubleEncode(bs, &cfg)
58+
if err == nil {
59+
return nil
60+
}
61+
62+
// Handle #16961
63+
if string(bs) != "&{}" {
64+
return err
65+
}
66+
case models.UnitTypeExternalWiki:
67+
cfg := &models.ExternalWikiConfig{}
68+
repoUnit.Config = cfg
69+
err := models.JSONUnmarshalHandleDoubleEncode(bs, &cfg)
70+
if err == nil {
71+
return nil
72+
}
73+
74+
if len(bs) < 3 {
75+
return err
76+
}
77+
if bs[0] != '&' || bs[1] != '{' || bs[len(bs)-1] != '}' {
78+
return err
79+
}
80+
cfg.ExternalWikiURL = string(bs[2 : len(bs)-1])
81+
case models.UnitTypeExternalTracker:
82+
cfg := &models.ExternalTrackerConfig{}
83+
repoUnit.Config = cfg
84+
err := models.JSONUnmarshalHandleDoubleEncode(bs, &cfg)
85+
if err == nil {
86+
return nil
87+
}
88+
// Handle #16961
89+
if len(bs) < 3 {
90+
return err
91+
}
92+
93+
if bs[0] != '&' || bs[1] != '{' || bs[len(bs)-1] != '}' {
94+
return err
95+
}
96+
97+
parts := bytes.Split(bs[2:len(bs)-1], []byte{' '})
98+
if len(parts) != 3 {
99+
return err
100+
}
101+
102+
cfg.ExternalTrackerURL = string(bytes.Join(parts[:len(parts)-2], []byte{' '}))
103+
cfg.ExternalTrackerFormat = string(parts[len(parts)-2])
104+
cfg.ExternalTrackerStyle = string(parts[len(parts)-1])
105+
case models.UnitTypePullRequests:
106+
cfg := &models.PullRequestsConfig{}
107+
repoUnit.Config = cfg
108+
err := models.JSONUnmarshalHandleDoubleEncode(bs, &cfg)
109+
if err == nil {
110+
return nil
111+
}
112+
113+
// Handle #16961
114+
if len(bs) < 3 {
115+
return err
116+
}
117+
118+
if bs[0] != '&' || bs[1] != '{' || bs[len(bs)-1] != '}' {
119+
return err
120+
}
121+
122+
// PullRequestsConfig was the following in 1.14
123+
// type PullRequestsConfig struct {
124+
// IgnoreWhitespaceConflicts bool
125+
// AllowMerge bool
126+
// AllowRebase bool
127+
// AllowRebaseMerge bool
128+
// AllowSquash bool
129+
// AllowManualMerge bool
130+
// AutodetectManualMerge bool
131+
// }
132+
//
133+
// 1.15 added in addition:
134+
// DefaultDeleteBranchAfterMerge bool
135+
// DefaultMergeStyle MergeStyle
136+
parts := bytes.Split(bs[2:len(bs)-1], []byte{' '})
137+
if len(parts) < 7 {
138+
return err
139+
}
140+
141+
var parseErr error
142+
cfg.IgnoreWhitespaceConflicts, parseErr = strconv.ParseBool(string(parts[0]))
143+
if parseErr != nil {
144+
return err
145+
}
146+
cfg.AllowMerge, parseErr = strconv.ParseBool(string(parts[1]))
147+
if parseErr != nil {
148+
return err
149+
}
150+
cfg.AllowRebase, parseErr = strconv.ParseBool(string(parts[2]))
151+
if parseErr != nil {
152+
return err
153+
}
154+
cfg.AllowRebaseMerge, parseErr = strconv.ParseBool(string(parts[3]))
155+
if parseErr != nil {
156+
return err
157+
}
158+
cfg.AllowSquash, parseErr = strconv.ParseBool(string(parts[4]))
159+
if parseErr != nil {
160+
return err
161+
}
162+
cfg.AllowManualMerge, parseErr = strconv.ParseBool(string(parts[5]))
163+
if parseErr != nil {
164+
return err
165+
}
166+
cfg.AutodetectManualMerge, parseErr = strconv.ParseBool(string(parts[6]))
167+
if parseErr != nil {
168+
return err
169+
}
170+
171+
// 1.14 unit
172+
if len(parts) == 7 {
173+
count++
174+
if !autofix {
175+
return nil
176+
}
177+
return models.UpdateRepoUnit(repoUnit)
178+
}
179+
180+
if len(parts) < 9 {
181+
return err
182+
}
183+
184+
cfg.DefaultDeleteBranchAfterMerge, parseErr = strconv.ParseBool(string(parts[7]))
185+
if parseErr != nil {
186+
return err
187+
}
188+
189+
cfg.DefaultMergeStyle = models.MergeStyle(string(bytes.Join(parts[8:], []byte{' '})))
190+
case models.UnitTypeIssues:
191+
cfg := &models.IssuesConfig{}
192+
repoUnit.Config = cfg
193+
err := models.JSONUnmarshalHandleDoubleEncode(bs, &cfg)
194+
if err == nil {
195+
return nil
196+
}
197+
198+
// Handle #16961
199+
if len(bs) < 3 {
200+
return err
201+
}
202+
203+
if bs[0] != '&' || bs[1] != '{' || bs[len(bs)-1] != '}' {
204+
return err
205+
}
206+
207+
parts := bytes.Split(bs[2:len(bs)-1], []byte{' '})
208+
if len(parts) != 3 {
209+
return err
210+
}
211+
var parseErr error
212+
cfg.EnableTimetracker, parseErr = strconv.ParseBool(string(parts[0]))
213+
if parseErr != nil {
214+
return err
215+
}
216+
cfg.AllowOnlyContributorsToTrackTime, parseErr = strconv.ParseBool(string(parts[1]))
217+
if parseErr != nil {
218+
return err
219+
}
220+
cfg.EnableDependencies, parseErr = strconv.ParseBool(string(parts[2]))
221+
if parseErr != nil {
222+
return err
223+
}
224+
default:
225+
panic(fmt.Sprintf("unrecognized repo unit type: %v", unit.Type))
226+
}
227+
228+
count++
229+
if !autofix {
230+
return nil
231+
}
232+
233+
return models.UpdateRepoUnit(repoUnit)
234+
},
235+
)
236+
237+
if err != nil {
238+
logger.Critical("Unable to iterate acrosss repounits to fix the broken units: Error %v", err)
239+
return err
240+
}
241+
242+
if !autofix {
243+
logger.Warn("Found %d broken repo_units", count)
244+
return nil
245+
}
246+
logger.Info("Fixed %d broken repo_units", count)
247+
248+
return nil
249+
}
250+
251+
func init() {
252+
Register(&Check{
253+
Title: "Check for incorrectly dumped repo_units (See #16961)",
254+
Name: "fix-broken-repo-units",
255+
IsDefault: false,
256+
Run: fixBrokenRepoUnits,
257+
Priority: 7,
258+
})
259+
}

0 commit comments

Comments
 (0)