@@ -35,6 +35,39 @@ export function extractRepoNameFromUrl(repoUrl: string): string {
3535 return parts [ parts . length - 1 ]
3636}
3737
38+ /**
39+ * Executes a git command with retry logic and exponential backoff
40+ */
41+ async function executeGitCommandWithRetry (
42+ command : string ,
43+ args : string [ ] ,
44+ options : any ,
45+ maxRetries : number = 3 ,
46+ baseDelay : number = 1000 ,
47+ ) : Promise < void > {
48+ let lastError : Error | undefined
49+
50+ for ( let attempt = 0 ; attempt < maxRetries ; attempt ++ ) {
51+ try {
52+ execFileSync ( command , args , options )
53+ return // Success!
54+ } catch ( error ) {
55+ lastError = error as Error
56+
57+ if ( attempt < maxRetries - 1 ) {
58+ const delay = baseDelay * Math . pow ( 2 , attempt )
59+ console . warn (
60+ `Git command failed (attempt ${ attempt + 1 } /${ maxRetries } ): ${ error instanceof Error ? error . message : String ( error ) } ` ,
61+ )
62+ console . warn ( `Retrying in ${ delay } ms...` )
63+ await new Promise ( ( resolve ) => setTimeout ( resolve , delay ) )
64+ }
65+ }
66+ }
67+
68+ throw lastError || new Error ( 'Git command failed after all retries' )
69+ }
70+
3871export async function setupTestRepo (
3972 repoUrl : string ,
4073 customRepoName : string ,
@@ -115,42 +148,64 @@ export async function setupTestRepo(
115148 ...process . env ,
116149 GIT_TERMINAL_PROMPT : '0' , // Disable interactive prompts
117150 GIT_ASKPASS : 'echo' , // Provide empty password if prompted
151+ GIT_HTTP_LOW_SPEED_LIMIT : '1000' , // Minimum speed (bytes/sec)
152+ GIT_HTTP_LOW_SPEED_TIME : '30' , // Time window for speed check (seconds)
118153 }
119154
120- execFileSync ( 'git' , [ 'clone' , '--no-checkout' , cloneUrl , repoDir ] , {
121- timeout : 120_000 , // 2 minute timeout for cloning
122- stdio : 'inherit' ,
123- env : gitEnv ,
124- } )
125- execFileSync ( 'git' , [ 'fetch' , 'origin' , commitSha ] , {
155+ await executeGitCommandWithRetry (
156+ 'git' ,
157+ [ 'clone' , '--no-checkout' , cloneUrl , repoDir ] ,
158+ {
159+ timeout : 600_000 , // 10 minute timeout for cloning
160+ stdio : 'inherit' ,
161+ env : gitEnv ,
162+ } ,
163+ )
164+ await executeGitCommandWithRetry ( 'git' , [ 'fetch' , 'origin' , commitSha ] , {
126165 cwd : repoDir ,
127166 stdio : 'inherit' ,
128167 env : gitEnv ,
129168 } )
130- execFileSync ( 'git' , [ 'checkout' , commitSha ] , {
169+ await executeGitCommandWithRetry ( 'git' , [ 'checkout' , commitSha ] , {
131170 cwd : repoDir ,
132171 stdio : 'inherit' ,
133172 } )
134173 } else {
135174 // Local development or public repos
136175 console . log ( `Local environment detected - cloning from: ${ repoUrl } ` )
137176
138- execFileSync (
177+ const localGitEnv = {
178+ ...process . env ,
179+ GIT_HTTP_LOW_SPEED_LIMIT : '1000' , // Minimum speed (bytes/sec)
180+ GIT_HTTP_LOW_SPEED_TIME : '30' , // Time window for speed check (seconds)
181+ }
182+
183+ await executeGitCommandWithRetry (
139184 'git' ,
140185 [ 'clone' , '--no-checkout' , '--quiet' , repoUrl , repoDir ] ,
141186 {
142- timeout : 120_000 , // 2 minute timeout for cloning
187+ timeout : 600_000 , // 10 minute timeout for cloning
188+ stdio : 'inherit' ,
189+ env : localGitEnv ,
190+ } ,
191+ )
192+ await executeGitCommandWithRetry (
193+ 'git' ,
194+ [ 'fetch' , 'origin' , '--quiet' , commitSha ] ,
195+ {
196+ cwd : repoDir ,
197+ stdio : 'inherit' ,
198+ env : localGitEnv ,
199+ } ,
200+ )
201+ await executeGitCommandWithRetry (
202+ 'git' ,
203+ [ 'checkout' , '--quiet' , commitSha ] ,
204+ {
205+ cwd : repoDir ,
143206 stdio : 'inherit' ,
144207 } ,
145208 )
146- execFileSync ( 'git' , [ 'fetch' , 'origin' , '--quiet' , commitSha ] , {
147- cwd : repoDir ,
148- stdio : 'inherit' ,
149- } )
150- execFileSync ( 'git' , [ 'checkout' , '--quiet' , commitSha ] , {
151- cwd : repoDir ,
152- stdio : 'inherit' ,
153- } )
154209 }
155210
156211 console . log ( 'Repository cloned successfully!' )
0 commit comments