11//
2- // Copyright (c) 2019-2025 Red Hat, Inc.
2+ // Copyright (c) 2019-2026 Red Hat, Inc.
33// Licensed under the Apache License, Version 2.0 (the "License");
44// you may not use this file except in compliance with the License.
55// You may obtain a copy of the License at
@@ -20,6 +20,7 @@ import (
2020 "log"
2121 "os"
2222 "path"
23+ "time"
2324
2425 dw "github.com/devfile/api/v2/pkg/apis/workspaces/v1alpha2"
2526 projectslib "github.com/devfile/devworkspace-operator/pkg/library/projects"
@@ -46,9 +47,24 @@ func doInitialGitClone(project *dw.Project) error {
4647 // Clone into a temp dir and then move set up project to PROJECTS_ROOT to try and make clone atomic in case
4748 // project-clone container is terminated
4849 tmpClonePath := path .Join (internal .CloneTmpDir , projectslib .GetClonePath (project ))
49- err := CloneProject (project , tmpClonePath )
50- if err != nil {
51- return fmt .Errorf ("failed to clone project: %s" , err )
50+ var cloneErr error
51+ for attempt := 0 ; attempt <= internal .CloneRetries ; attempt ++ {
52+ if attempt > 0 {
53+ delayBeforeRetry (project .Name , attempt )
54+ if err := os .RemoveAll (tmpClonePath ); err != nil {
55+ log .Printf ("Warning: cleanup before retry failed: %s" , err )
56+ }
57+ }
58+ cloneErr = CloneProject (project , tmpClonePath )
59+ if cloneErr == nil {
60+ break
61+ }
62+ if attempt < internal .CloneRetries {
63+ log .Printf ("Failed git clone for project %s (attempt %d/%d): %s" , project .Name , attempt + 1 , internal .CloneRetries + 1 , cloneErr )
64+ }
65+ }
66+ if cloneErr != nil {
67+ return fmt .Errorf ("failed to clone project: %w" , cloneErr )
5268 }
5369
5470 if project .Attributes .Exists (internal .ProjectSparseCheckout ) {
@@ -83,6 +99,12 @@ func doInitialGitClone(project *dw.Project) error {
8399 return nil
84100}
85101
102+ func delayBeforeRetry (projectName string , attempt int ) {
103+ delay := internal .BaseRetryDelay * (1 << (attempt - 1 ))
104+ log .Printf ("Retrying git clone for project %s (attempt %d/%d) after %s" , projectName , attempt + 1 , internal .CloneRetries + 1 , delay )
105+ time .Sleep (delay )
106+ }
107+
86108func setupRemotesForExistingProject (project * dw.Project ) error {
87109 projectPath := path .Join (internal .ProjectsRoot , projectslib .GetClonePath (project ))
88110 repo , err := internal .OpenRepo (projectPath )
0 commit comments