@@ -141,6 +141,43 @@ func parseRemoteUpdateOutput(output string) []*mirrorSyncResult {
141
141
return results
142
142
}
143
143
144
+ func pruneBrokenReferences (ctx context.Context ,
145
+ m * models.Mirror ,
146
+ repoPath string ,
147
+ timeout time.Duration ,
148
+ stdoutBuilder , stderrBuilder * strings.Builder ,
149
+ sanitizer * strings.Replacer ,
150
+ isWiki bool ) error {
151
+
152
+ wiki := ""
153
+ if isWiki {
154
+ wiki = "Wiki "
155
+ }
156
+
157
+ stderrBuilder .Reset ()
158
+ stdoutBuilder .Reset ()
159
+ pruneErr := git .NewCommandContext (ctx , "remote" , "prune" , m .GetRemoteName ()).
160
+ SetDescription (fmt .Sprintf ("Mirror.runSync %ssPrune references: %s " , wiki , m .Repo .FullName ())).
161
+ RunInDirTimeoutPipeline (timeout , repoPath , stdoutBuilder , stderrBuilder )
162
+ if pruneErr != nil {
163
+ stdout := stdoutBuilder .String ()
164
+ stderr := stderrBuilder .String ()
165
+
166
+ // sanitize the output, since it may contain the remote address, which may
167
+ // contain a password
168
+ stderrMessage := sanitizer .Replace (stderr )
169
+ stdoutMessage := sanitizer .Replace (stdout )
170
+
171
+ log .Error ("Failed to prune mirror repository %s%-v references:\n Stdout: %s\n Stderr: %s\n Err: %v" , wiki , m .Repo , stdoutMessage , stderrMessage , pruneErr )
172
+ desc := fmt .Sprintf ("Failed to prune mirror repository %s'%s' references: %s" , wiki , repoPath , stderrMessage )
173
+ if err := models .CreateRepositoryNotice (desc ); err != nil {
174
+ log .Error ("CreateRepositoryNotice: %v" , err )
175
+ }
176
+ // this if will only be reached on a successful prune so try to get the mirror again
177
+ }
178
+ return pruneErr
179
+ }
180
+
144
181
// runSync returns true if sync finished without error.
145
182
func runSync (ctx context.Context , m * models.Mirror ) ([]* mirrorSyncResult , bool ) {
146
183
repoPath := m .Repo .RepoPath ()
@@ -161,25 +198,52 @@ func runSync(ctx context.Context, m *models.Mirror) ([]*mirrorSyncResult, bool)
161
198
162
199
stdoutBuilder := strings.Builder {}
163
200
stderrBuilder := strings.Builder {}
164
- if err := git .NewCommand ( gitArgs ... ).
201
+ if err := git .NewCommandContext ( ctx , gitArgs ... ).
165
202
SetDescription (fmt .Sprintf ("Mirror.runSync: %s" , m .Repo .FullName ())).
166
203
RunInDirTimeoutPipeline (timeout , repoPath , & stdoutBuilder , & stderrBuilder ); err != nil {
167
204
stdout := stdoutBuilder .String ()
168
205
stderr := stderrBuilder .String ()
169
206
170
207
// sanitize the output, since it may contain the remote address, which may
171
208
// contain a password
172
-
173
209
sanitizer := util .NewURLSanitizer (remoteAddr , true )
174
210
stderrMessage := sanitizer .Replace (stderr )
175
211
stdoutMessage := sanitizer .Replace (stdout )
176
212
177
- log .Error ("Failed to update mirror repository %v:\n Stdout: %s\n Stderr: %s\n Err: %v" , m .Repo , stdoutMessage , stderrMessage , err )
178
- desc := fmt .Sprintf ("Failed to update mirror repository '%s': %s" , repoPath , stderrMessage )
179
- if err = models .CreateRepositoryNotice (desc ); err != nil {
180
- log .Error ("CreateRepositoryNotice: %v" , err )
213
+ // Now check if the error is a resolve reference due to broken reference
214
+ if strings .Contains (stderr , "unable to resolve reference" ) && strings .Contains (stderr , "reference broken" ) {
215
+ log .Warn ("Failed to update mirror repository %-v due to broken references:\n Stdout: %s\n Stderr: %s\n Err: %v\n Attempting Prune" , m .Repo , stdoutMessage , stderrMessage , err )
216
+ err = nil
217
+
218
+ // Attempt prune
219
+ pruneErr := pruneBrokenReferences (ctx , m , repoPath , timeout , & stdoutBuilder , & stderrBuilder , sanitizer , false )
220
+ if pruneErr == nil {
221
+ // Successful prune - reattempt mirror
222
+ stderrBuilder .Reset ()
223
+ stdoutBuilder .Reset ()
224
+ if err = git .NewCommandContext (ctx , gitArgs ... ).
225
+ SetDescription (fmt .Sprintf ("Mirror.runSync: %s" , m .Repo .FullName ())).
226
+ RunInDirTimeoutPipeline (timeout , repoPath , & stdoutBuilder , & stderrBuilder ); err != nil {
227
+ stdout := stdoutBuilder .String ()
228
+ stderr := stderrBuilder .String ()
229
+
230
+ // sanitize the output, since it may contain the remote address, which may
231
+ // contain a password
232
+ stderrMessage = sanitizer .Replace (stderr )
233
+ stdoutMessage = sanitizer .Replace (stdout )
234
+ }
235
+ }
236
+ }
237
+
238
+ // If there is still an error (or there always was an error)
239
+ if err != nil {
240
+ log .Error ("Failed to update mirror repository %-v:\n Stdout: %s\n Stderr: %s\n Err: %v" , m .Repo , stdoutMessage , stderrMessage , err )
241
+ desc := fmt .Sprintf ("Failed to update mirror repository '%s': %s" , repoPath , stderrMessage )
242
+ if err = models .CreateRepositoryNotice (desc ); err != nil {
243
+ log .Error ("CreateRepositoryNotice: %v" , err )
244
+ }
245
+ return nil , false
181
246
}
182
- return nil , false
183
247
}
184
248
output := stderrBuilder .String ()
185
249
@@ -212,7 +276,7 @@ func runSync(ctx context.Context, m *models.Mirror) ([]*mirrorSyncResult, bool)
212
276
log .Trace ("SyncMirrors [repo: %-v Wiki]: running git remote update..." , m .Repo )
213
277
stderrBuilder .Reset ()
214
278
stdoutBuilder .Reset ()
215
- if err := git .NewCommand ( "remote" , "update" , "--prune" , m .GetRemoteName ()).
279
+ if err := git .NewCommandContext ( ctx , "remote" , "update" , "--prune" , m .GetRemoteName ()).
216
280
SetDescription (fmt .Sprintf ("Mirror.runSync Wiki: %s " , m .Repo .FullName ())).
217
281
RunInDirTimeoutPipeline (timeout , wikiPath , & stdoutBuilder , & stderrBuilder ); err != nil {
218
282
stdout := stdoutBuilder .String ()
@@ -226,16 +290,44 @@ func runSync(ctx context.Context, m *models.Mirror) ([]*mirrorSyncResult, bool)
226
290
log .Error ("GetRemoteAddress Error %v" , remoteErr )
227
291
}
228
292
293
+ // sanitize the output, since it may contain the remote address, which may
294
+ // contain a password
229
295
sanitizer := util .NewURLSanitizer (remoteAddr , true )
230
296
stderrMessage := sanitizer .Replace (stderr )
231
297
stdoutMessage := sanitizer .Replace (stdout )
232
298
233
- log .Error ("Failed to update mirror repository wiki %v:\n Stdout: %s\n Stderr: %s\n Err: %v" , m .Repo , stdoutMessage , stderrMessage , err )
234
- desc := fmt .Sprintf ("Failed to update mirror repository wiki '%s': %s" , wikiPath , stderrMessage )
235
- if err = models .CreateRepositoryNotice (desc ); err != nil {
236
- log .Error ("CreateRepositoryNotice: %v" , err )
299
+ // Now check if the error is a resolve reference due to broken reference
300
+ if strings .Contains (stderrMessage , "unable to resolve reference" ) && strings .Contains (stderrMessage , "reference broken" ) {
301
+ log .Warn ("Failed to update mirror wiki repository %-v due to broken references:\n Stdout: %s\n Stderr: %s\n Err: %v\n Attempting Prune" , m .Repo , stdoutMessage , stderrMessage , err )
302
+ err = nil
303
+
304
+ // Attempt prune
305
+ pruneErr := pruneBrokenReferences (ctx , m , repoPath , timeout , & stdoutBuilder , & stderrBuilder , sanitizer , true )
306
+ if pruneErr == nil {
307
+ // Successful prune - reattempt mirror
308
+ stderrBuilder .Reset ()
309
+ stdoutBuilder .Reset ()
310
+
311
+ if err = git .NewCommandContext (ctx , "remote" , "update" , "--prune" , m .GetRemoteName ()).
312
+ SetDescription (fmt .Sprintf ("Mirror.runSync Wiki: %s " , m .Repo .FullName ())).
313
+ RunInDirTimeoutPipeline (timeout , wikiPath , & stdoutBuilder , & stderrBuilder ); err != nil {
314
+ stdout := stdoutBuilder .String ()
315
+ stderr := stderrBuilder .String ()
316
+ stderrMessage = sanitizer .Replace (stderr )
317
+ stdoutMessage = sanitizer .Replace (stdout )
318
+ }
319
+ }
320
+ }
321
+
322
+ // If there is still an error (or there always was an error)
323
+ if err != nil {
324
+ log .Error ("Failed to update mirror repository wiki %-v:\n Stdout: %s\n Stderr: %s\n Err: %v" , m .Repo , stdoutMessage , stderrMessage , err )
325
+ desc := fmt .Sprintf ("Failed to update mirror repository wiki '%s': %s" , wikiPath , stderrMessage )
326
+ if err = models .CreateRepositoryNotice (desc ); err != nil {
327
+ log .Error ("CreateRepositoryNotice: %v" , err )
328
+ }
329
+ return nil , false
237
330
}
238
- return nil , false
239
331
}
240
332
log .Trace ("SyncMirrors [repo: %-v Wiki]: git remote update complete" , m .Repo )
241
333
}
0 commit comments