gitea

Зеркало из https://github.com/go-gitea/gitea
Форк
0
/
fix16961.go 
328 строк · 8.0 Кб
1
// Copyright 2021 The Gitea Authors. All rights reserved.
2
// SPDX-License-Identifier: MIT
3

4
package doctor
5

6
import (
7
	"bytes"
8
	"context"
9
	"errors"
10
	"fmt"
11

12
	"code.gitea.io/gitea/models/db"
13
	repo_model "code.gitea.io/gitea/models/repo"
14
	"code.gitea.io/gitea/models/unit"
15
	"code.gitea.io/gitea/modules/json"
16
	"code.gitea.io/gitea/modules/log"
17
	"code.gitea.io/gitea/modules/timeutil"
18

19
	"xorm.io/builder"
20
)
21

22
// #16831 revealed that the dump command that was broken in 1.14.3-1.14.6 and 1.15.0 (#15885).
23
// This led to repo_unit and login_source cfg not being converted to JSON in the dump
24
// Unfortunately although it was hoped that there were only a few users affected it
25
// appears that many users are affected.
26

27
// We therefore need to provide a doctor command to fix this repeated issue #16961
28

29
func parseBool16961(bs []byte) (bool, error) {
30
	if bytes.EqualFold(bs, []byte("%!s(bool=false)")) {
31
		return false, nil
32
	}
33

34
	if bytes.EqualFold(bs, []byte("%!s(bool=true)")) {
35
		return true, nil
36
	}
37

38
	return false, fmt.Errorf("unexpected bool format: %s", string(bs))
39
}
40

41
func fixUnitConfig16961(bs []byte, cfg *repo_model.UnitConfig) (fixed bool, err error) {
42
	err = json.UnmarshalHandleDoubleEncode(bs, &cfg)
43
	if err == nil {
44
		return false, nil
45
	}
46

47
	// Handle #16961
48
	if string(bs) != "&{}" && len(bs) != 0 {
49
		return false, err
50
	}
51

52
	return true, nil
53
}
54

55
func fixExternalWikiConfig16961(bs []byte, cfg *repo_model.ExternalWikiConfig) (fixed bool, err error) {
56
	err = json.UnmarshalHandleDoubleEncode(bs, &cfg)
57
	if err == nil {
58
		return false, nil
59
	}
60

61
	if len(bs) < 3 {
62
		return false, err
63
	}
64
	if bs[0] != '&' || bs[1] != '{' || bs[len(bs)-1] != '}' {
65
		return false, err
66
	}
67
	cfg.ExternalWikiURL = string(bs[2 : len(bs)-1])
68
	return true, nil
69
}
70

71
func fixExternalTrackerConfig16961(bs []byte, cfg *repo_model.ExternalTrackerConfig) (fixed bool, err error) {
72
	err = json.UnmarshalHandleDoubleEncode(bs, &cfg)
73
	if err == nil {
74
		return false, nil
75
	}
76
	// Handle #16961
77
	if len(bs) < 3 {
78
		return false, err
79
	}
80

81
	if bs[0] != '&' || bs[1] != '{' || bs[len(bs)-1] != '}' {
82
		return false, err
83
	}
84

85
	parts := bytes.Split(bs[2:len(bs)-1], []byte{' '})
86
	if len(parts) != 3 {
87
		return false, err
88
	}
89

90
	cfg.ExternalTrackerURL = string(bytes.Join(parts[:len(parts)-2], []byte{' '}))
91
	cfg.ExternalTrackerFormat = string(parts[len(parts)-2])
92
	cfg.ExternalTrackerStyle = string(parts[len(parts)-1])
93
	return true, nil
94
}
95

96
func fixPullRequestsConfig16961(bs []byte, cfg *repo_model.PullRequestsConfig) (fixed bool, err error) {
97
	err = json.UnmarshalHandleDoubleEncode(bs, &cfg)
98
	if err == nil {
99
		return false, nil
100
	}
101

102
	// Handle #16961
103
	if len(bs) < 3 {
104
		return false, err
105
	}
106

107
	if bs[0] != '&' || bs[1] != '{' || bs[len(bs)-1] != '}' {
108
		return false, err
109
	}
110

111
	// PullRequestsConfig was the following in 1.14
112
	// type PullRequestsConfig struct {
113
	// 	IgnoreWhitespaceConflicts bool
114
	// 	AllowMerge                bool
115
	// 	AllowRebase               bool
116
	// 	AllowRebaseMerge          bool
117
	// 	AllowSquash               bool
118
	// 	AllowManualMerge          bool
119
	// 	AutodetectManualMerge     bool
120
	// }
121
	//
122
	// 1.15 added in addition:
123
	// DefaultDeleteBranchAfterMerge bool
124
	// DefaultMergeStyle             MergeStyle
125
	parts := bytes.Split(bs[2:len(bs)-1], []byte{' '})
126
	if len(parts) < 7 {
127
		return false, err
128
	}
129

130
	var parseErr error
131
	cfg.IgnoreWhitespaceConflicts, parseErr = parseBool16961(parts[0])
132
	if parseErr != nil {
133
		return false, errors.Join(err, parseErr)
134
	}
135
	cfg.AllowMerge, parseErr = parseBool16961(parts[1])
136
	if parseErr != nil {
137
		return false, errors.Join(err, parseErr)
138
	}
139
	cfg.AllowRebase, parseErr = parseBool16961(parts[2])
140
	if parseErr != nil {
141
		return false, errors.Join(err, parseErr)
142
	}
143
	cfg.AllowRebaseMerge, parseErr = parseBool16961(parts[3])
144
	if parseErr != nil {
145
		return false, errors.Join(err, parseErr)
146
	}
147
	cfg.AllowSquash, parseErr = parseBool16961(parts[4])
148
	if parseErr != nil {
149
		return false, errors.Join(err, parseErr)
150
	}
151
	cfg.AllowManualMerge, parseErr = parseBool16961(parts[5])
152
	if parseErr != nil {
153
		return false, errors.Join(err, parseErr)
154
	}
155
	cfg.AutodetectManualMerge, parseErr = parseBool16961(parts[6])
156
	if parseErr != nil {
157
		return false, errors.Join(err, parseErr)
158
	}
159

160
	// 1.14 unit
161
	if len(parts) == 7 {
162
		return true, nil
163
	}
164

165
	if len(parts) < 9 {
166
		return false, err
167
	}
168

169
	cfg.DefaultDeleteBranchAfterMerge, parseErr = parseBool16961(parts[7])
170
	if parseErr != nil {
171
		return false, errors.Join(err, parseErr)
172
	}
173

174
	cfg.DefaultMergeStyle = repo_model.MergeStyle(string(bytes.Join(parts[8:], []byte{' '})))
175
	return true, nil
176
}
177

178
func fixIssuesConfig16961(bs []byte, cfg *repo_model.IssuesConfig) (fixed bool, err error) {
179
	err = json.UnmarshalHandleDoubleEncode(bs, &cfg)
180
	if err == nil {
181
		return false, nil
182
	}
183

184
	// Handle #16961
185
	if len(bs) < 3 {
186
		return false, err
187
	}
188

189
	if bs[0] != '&' || bs[1] != '{' || bs[len(bs)-1] != '}' {
190
		return false, err
191
	}
192

193
	parts := bytes.Split(bs[2:len(bs)-1], []byte{' '})
194
	if len(parts) != 3 {
195
		return false, err
196
	}
197
	var parseErr error
198
	cfg.EnableTimetracker, parseErr = parseBool16961(parts[0])
199
	if parseErr != nil {
200
		return false, errors.Join(err, parseErr)
201
	}
202
	cfg.AllowOnlyContributorsToTrackTime, parseErr = parseBool16961(parts[1])
203
	if parseErr != nil {
204
		return false, errors.Join(err, parseErr)
205
	}
206
	cfg.EnableDependencies, parseErr = parseBool16961(parts[2])
207
	if parseErr != nil {
208
		return false, errors.Join(err, parseErr)
209
	}
210
	return true, nil
211
}
212

213
func fixBrokenRepoUnit16961(repoUnit *repo_model.RepoUnit, bs []byte) (fixed bool, err error) {
214
	// Shortcut empty or null values
215
	if len(bs) == 0 {
216
		return false, nil
217
	}
218

219
	var cfg any
220
	err = json.UnmarshalHandleDoubleEncode(bs, &cfg)
221
	if err == nil {
222
		return false, nil
223
	}
224

225
	switch repoUnit.Type {
226
	case unit.TypeCode, unit.TypeReleases, unit.TypeWiki, unit.TypeProjects:
227
		cfg := &repo_model.UnitConfig{}
228
		repoUnit.Config = cfg
229
		if fixed, err := fixUnitConfig16961(bs, cfg); !fixed {
230
			return false, err
231
		}
232
	case unit.TypeExternalWiki:
233
		cfg := &repo_model.ExternalWikiConfig{}
234
		repoUnit.Config = cfg
235

236
		if fixed, err := fixExternalWikiConfig16961(bs, cfg); !fixed {
237
			return false, err
238
		}
239
	case unit.TypeExternalTracker:
240
		cfg := &repo_model.ExternalTrackerConfig{}
241
		repoUnit.Config = cfg
242
		if fixed, err := fixExternalTrackerConfig16961(bs, cfg); !fixed {
243
			return false, err
244
		}
245
	case unit.TypePullRequests:
246
		cfg := &repo_model.PullRequestsConfig{}
247
		repoUnit.Config = cfg
248

249
		if fixed, err := fixPullRequestsConfig16961(bs, cfg); !fixed {
250
			return false, err
251
		}
252
	case unit.TypeIssues:
253
		cfg := &repo_model.IssuesConfig{}
254
		repoUnit.Config = cfg
255
		if fixed, err := fixIssuesConfig16961(bs, cfg); !fixed {
256
			return false, err
257
		}
258
	default:
259
		panic(fmt.Sprintf("unrecognized repo unit type: %v", repoUnit.Type))
260
	}
261
	return true, nil
262
}
263

264
func fixBrokenRepoUnits16961(ctx context.Context, logger log.Logger, autofix bool) error {
265
	// RepoUnit describes all units of a repository
266
	type RepoUnit struct {
267
		ID          int64
268
		RepoID      int64
269
		Type        unit.Type
270
		Config      []byte
271
		CreatedUnix timeutil.TimeStamp `xorm:"INDEX CREATED"`
272
	}
273

274
	count := 0
275

276
	err := db.Iterate(
277
		ctx,
278
		builder.Gt{
279
			"id": 0,
280
		},
281
		func(ctx context.Context, unit *RepoUnit) error {
282
			bs := unit.Config
283
			repoUnit := &repo_model.RepoUnit{
284
				ID:          unit.ID,
285
				RepoID:      unit.RepoID,
286
				Type:        unit.Type,
287
				CreatedUnix: unit.CreatedUnix,
288
			}
289

290
			if fixed, err := fixBrokenRepoUnit16961(repoUnit, bs); !fixed {
291
				return err
292
			}
293

294
			count++
295
			if !autofix {
296
				return nil
297
			}
298

299
			return repo_model.UpdateRepoUnit(ctx, repoUnit)
300
		},
301
	)
302
	if err != nil {
303
		logger.Critical("Unable to iterate across repounits to fix the broken units: Error %v", err)
304
		return err
305
	}
306

307
	if !autofix {
308
		if count == 0 {
309
			logger.Info("Found no broken repo_units")
310
		} else {
311
			logger.Warn("Found %d broken repo_units", count)
312
		}
313
		return nil
314
	}
315
	logger.Info("Fixed %d broken repo_units", count)
316

317
	return nil
318
}
319

320
func init() {
321
	Register(&Check{
322
		Title:     "Check for incorrectly dumped repo_units (See #16961)",
323
		Name:      "fix-broken-repo-units",
324
		IsDefault: false,
325
		Run:       fixBrokenRepoUnits16961,
326
		Priority:  7,
327
	})
328
}
329

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.