Skip to content

Commit 2d2f32d

Browse files
committed
Handle broken references in mirror sync
If there are broken references during a mirror attempt to fix using `git remote prune`. Signed-off-by: Andrew Thornton <[email protected]>
1 parent 9a938dc commit 2d2f32d

File tree

1 file changed

+89
-11
lines changed

1 file changed

+89
-11
lines changed

services/mirror/mirror_pull.go

Lines changed: 89 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -168,17 +168,57 @@ func runSync(ctx context.Context, m *models.Mirror) ([]*mirrorSyncResult, bool)
168168

169169
// sanitize the output, since it may contain the remote address, which may
170170
// contain a password
171-
172171
sanitizer := util.NewURLSanitizer(remoteAddr, true)
173172
stderrMessage := sanitizer.Replace(stderr)
174173
stdoutMessage := sanitizer.Replace(stdout)
175174

176-
log.Error("Failed to update mirror repository %v:\nStdout: %s\nStderr: %s\nErr: %v", m.Repo, stdoutMessage, stderrMessage, err)
177-
desc := fmt.Sprintf("Failed to update mirror repository '%s': %s", repoPath, stderrMessage)
178-
if err = models.CreateRepositoryNotice(desc); err != nil {
179-
log.Error("CreateRepositoryNotice: %v", err)
175+
// Now check if the error is a resolve reference due to broken reference
176+
if strings.Contains(stderr, "unable to resolve reference") && strings.Contains(stderr, "reference broken") {
177+
log.Warn("Failed to update mirror repository %-v due to broken references:\nStdout: %s\nStderr: %s\nErr: %v\nAttempting Prune", m.Repo, stdoutMessage, stderrMessage, err)
178+
err = nil
179+
180+
// Attempt prune
181+
stderrBuilder.Reset()
182+
stdoutBuilder.Reset()
183+
if pruneErr := git.NewCommand("remote", "prune", m.GetRemoteName()).
184+
SetDescription(fmt.Sprintf("Mirror.runSync Prune references: %s ", m.Repo.FullName())).
185+
RunInDirTimeoutPipeline(timeout, repoPath, &stdoutBuilder, &stderrBuilder); pruneErr != nil {
186+
stdout := stdoutBuilder.String()
187+
stderr := stderrBuilder.String()
188+
189+
// sanitize the output, since it may contain the remote address, which may
190+
// contain a password
191+
stderrMessage := sanitizer.Replace(stderr)
192+
stdoutMessage := sanitizer.Replace(stdout)
193+
194+
log.Error("Failed to prune mirror repository %-v references:\nStdout: %s\nStderr: %s\nErr: %v", m.Repo, stdoutMessage, stderrMessage, pruneErr)
195+
desc := fmt.Sprintf("Failed to prune mirror repository '%s' references: %s", repoPath, stderrMessage)
196+
if err := models.CreateRepositoryNotice(desc); err != nil {
197+
log.Error("CreateRepositoryNotice: %v", err)
198+
}
199+
// this if will only be reached on a successful prune so try to get the mirror again
200+
} else if err = git.NewCommand(gitArgs...).
201+
SetDescription(fmt.Sprintf("Mirror.runSync: %s", m.Repo.FullName())).
202+
RunInDirTimeoutPipeline(timeout, repoPath, &stdoutBuilder, &stderrBuilder); err != nil {
203+
stdout := stdoutBuilder.String()
204+
stderr := stderrBuilder.String()
205+
206+
// sanitize the output, since it may contain the remote address, which may
207+
// contain a password
208+
stderrMessage = sanitizer.Replace(stderr)
209+
stdoutMessage = sanitizer.Replace(stdout)
210+
}
211+
}
212+
213+
// If there is still an error (or there always was an error)
214+
if err != nil {
215+
log.Error("Failed to update mirror repository %-v:\nStdout: %s\nStderr: %s\nErr: %v", m.Repo, stdoutMessage, stderrMessage, err)
216+
desc := fmt.Sprintf("Failed to update mirror repository '%s': %s", repoPath, stderrMessage)
217+
if err = models.CreateRepositoryNotice(desc); err != nil {
218+
log.Error("CreateRepositoryNotice: %v", err)
219+
}
220+
return nil, false
180221
}
181-
return nil, false
182222
}
183223
output := stderrBuilder.String()
184224

@@ -225,16 +265,54 @@ func runSync(ctx context.Context, m *models.Mirror) ([]*mirrorSyncResult, bool)
225265
log.Error("GetRemoteAddress Error %v", remoteErr)
226266
}
227267

268+
// sanitize the output, since it may contain the remote address, which may
269+
// contain a password
228270
sanitizer := util.NewURLSanitizer(remoteAddr, true)
229271
stderrMessage := sanitizer.Replace(stderr)
230272
stdoutMessage := sanitizer.Replace(stdout)
231273

232-
log.Error("Failed to update mirror repository wiki %v:\nStdout: %s\nStderr: %s\nErr: %v", m.Repo, stdoutMessage, stderrMessage, err)
233-
desc := fmt.Sprintf("Failed to update mirror repository wiki '%s': %s", wikiPath, stderrMessage)
234-
if err = models.CreateRepositoryNotice(desc); err != nil {
235-
log.Error("CreateRepositoryNotice: %v", err)
274+
// Now check if the error is a resolve reference due to broken reference
275+
if strings.Contains(stderrMessage, "unable to resolve reference") && strings.Contains(stderrMessage, "reference broken") {
276+
log.Warn("Failed to update mirror wiki repository %-v due to broken references:\nStdout: %s\nStderr: %s\nErr: %v\nAttempting Prune", m.Repo, stdoutMessage, stderrMessage, err)
277+
err = nil
278+
279+
// Attempt prune
280+
stderrBuilder.Reset()
281+
stdoutBuilder.Reset()
282+
if err := git.NewCommand("remote", "prune", m.GetRemoteName()).
283+
SetDescription(fmt.Sprintf("Mirror.runSync Prune Wiki references: %s ", m.Repo.FullName())).
284+
RunInDirTimeoutPipeline(timeout, wikiPath, &stdoutBuilder, &stderrBuilder); err != nil {
285+
stdout := stdoutBuilder.String()
286+
stderr := stderrBuilder.String()
287+
288+
stderrMessage := sanitizer.Replace(stderr)
289+
stdoutMessage := sanitizer.Replace(stdout)
290+
291+
log.Error("Failed to prune mirror repository wiki %-v references:\nStdout: %s\nStderr: %s\nErr: %v", m.Repo, stdoutMessage, stderrMessage, err)
292+
desc := fmt.Sprintf("Failed to prune mirror repository wiki '%s' references: %s", wikiPath, stderrMessage)
293+
if err = models.CreateRepositoryNotice(desc); err != nil {
294+
log.Error("CreateRepositoryNotice: %v", err)
295+
}
296+
// this if will only be reached on a successful prune so try to get the mirror again
297+
} else if err = git.NewCommand("remote", "update", "--prune", m.GetRemoteName()).
298+
SetDescription(fmt.Sprintf("Mirror.runSync Wiki: %s ", m.Repo.FullName())).
299+
RunInDirTimeoutPipeline(timeout, wikiPath, &stdoutBuilder, &stderrBuilder); err != nil {
300+
stdout := stdoutBuilder.String()
301+
stderr := stderrBuilder.String()
302+
stderrMessage = sanitizer.Replace(stderr)
303+
stdoutMessage = sanitizer.Replace(stdout)
304+
}
305+
}
306+
307+
// If there is still an error (or there always was an error)
308+
if err != nil {
309+
log.Error("Failed to update mirror repository wiki %-v:\nStdout: %s\nStderr: %s\nErr: %v", m.Repo, stdoutMessage, stderrMessage, err)
310+
desc := fmt.Sprintf("Failed to update mirror repository wiki '%s': %s", wikiPath, stderrMessage)
311+
if err = models.CreateRepositoryNotice(desc); err != nil {
312+
log.Error("CreateRepositoryNotice: %v", err)
313+
}
314+
return nil, false
236315
}
237-
return nil, false
238316
}
239317
log.Trace("SyncMirrors [repo: %-v Wiki]: git remote update complete", m.Repo)
240318
}

0 commit comments

Comments
 (0)