Skip to content

Commit bc077f9

Browse files
petr-mullerclaude
andcommitted
fix(private-org-sync): retry all git operations on transient network errors
Wrap gitExec with withRetryOnTransientError to transparently retry any git command (fetch, push, ls-remote, etc.) up to 3 times with a 5-second delay when transient network errors are detected (DNS resolution failures, connection timeouts/refused/reset, server 5xx errors). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 48f29ee commit bc077f9

1 file changed

Lines changed: 39 additions & 1 deletion

File tree

cmd/private-org-sync/main.go

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,27 @@ func withRetryOnNonzero(f gitFunc, retries int) gitFunc {
170170
}
171171
}
172172

173+
func withRetryOnTransientError(f gitFunc, retries int) gitFunc {
174+
return func(logger *logrus.Entry, dir string, command ...string) (string, int, error) {
175+
var out string
176+
var exitCode int
177+
var commandErr error
178+
for attempt := 1; attempt <= retries; attempt++ {
179+
out, exitCode, commandErr = f(logger, dir, command...)
180+
if commandErr == nil && exitCode == 0 {
181+
return out, exitCode, nil
182+
}
183+
if attempt < retries && isTransientNetworkError(out) {
184+
logger.Infof("Transient network error, retrying git command (%d/%d)", attempt, retries)
185+
time.Sleep(5 * time.Second)
186+
continue
187+
}
188+
break
189+
}
190+
return out, exitCode, commandErr
191+
}
192+
}
193+
173194
func gitExec(logger *logrus.Entry, dir string, command ...string) (string, int, error) {
174195
cmdLogger := logger.WithField("command", fmt.Sprintf("git %s", strings.Join(command, " ")))
175196
cmd := exec.Command("git", command...)
@@ -319,6 +340,23 @@ func maybeTooShallow(pushOutput string) bool {
319340
return false
320341
}
321342

343+
func isTransientNetworkError(output string) bool {
344+
patterns := []string{
345+
"Could not resolve host",
346+
"Failed to connect to",
347+
"Connection timed out",
348+
"Connection refused",
349+
"Connection reset by peer",
350+
"The requested URL returned error: 5",
351+
}
352+
for _, pattern := range patterns {
353+
if strings.Contains(output, pattern) {
354+
return true
355+
}
356+
}
357+
return false
358+
}
359+
322360
// location specifies a GitHub repository branch used as a source or destination
323361
type location struct {
324362
org, repo, branch string
@@ -611,7 +649,7 @@ func main() {
611649
token: token,
612650
root: o.gitDir,
613651
confirm: o.confirm,
614-
git: gitExec,
652+
git: withRetryOnTransientError(gitExec, 3),
615653
failOnNonexistentDst: o.failOnNonexistentDst,
616654
gitName: o.gitName,
617655
gitEmail: o.gitEmail,

0 commit comments

Comments
 (0)