@@ -11,12 +11,19 @@ import (
11
11
"os"
12
12
"regexp"
13
13
"strings"
14
+ "time"
14
15
16
+ "github.com/cenkalti/backoff/v4"
15
17
"github.com/go-git/go-git/v5"
16
18
"github.com/go-git/go-git/v5/plumbing"
17
19
"github.com/go-git/go-git/v5/plumbing/transport/http"
18
20
)
19
21
22
+ const (
23
+ maxRetries = 3
24
+ backoffInterval = time .Second * 1
25
+ )
26
+
20
27
// New returns a new cloner.
21
28
func New (depth int , stdout io.Writer ) Cloner {
22
29
c := & cloner {
@@ -68,24 +75,48 @@ func (c *cloner) Clone(ctx context.Context, params Params) error {
68
75
}
69
76
}
70
77
// clone the repository
71
- r , err := git .PlainClone (params .Dir , false , opts )
72
- if (errors .Is (plumbing .ErrReferenceNotFound , err ) || matchRefNotFoundErr (err )) &&
73
- ! strings .HasPrefix (params .Ref , "refs/" ) {
74
- // If params.Ref is provided without refs/*, then we are assuming it to either refs/heads/ or refs/tags.
75
- // Try clone again with inverse ref.
76
- if opts .ReferenceName .IsBranch () {
77
- opts .ReferenceName = plumbing .ReferenceName ("refs/tags/" + params .Ref )
78
- } else if opts .ReferenceName .IsTag () {
79
- opts .ReferenceName = plumbing .ReferenceName ("refs/heads/" + params .Ref )
80
- } else {
81
- return err
82
- }
78
+ var (
79
+ r * git.Repository
80
+ err error
81
+ )
82
+
83
+ retryStrategy := backoff .NewExponentialBackOff ()
84
+ retryStrategy .InitialInterval = backoffInterval
85
+ retryStrategy .MaxInterval = backoffInterval * 5 // Maximum delay
86
+ retryStrategy .MaxElapsedTime = backoffInterval * 60 // Maximum time to retry (1min)
87
+
88
+ b := backoff .WithMaxRetries (retryStrategy , uint64 (maxRetries ))
83
89
90
+ err = backoff .Retry (func () error {
84
91
r , err = git .PlainClone (params .Dir , false , opts )
85
- if err ! = nil {
86
- return err
92
+ if err = = nil {
93
+ return nil
87
94
}
88
- } else if err != nil {
95
+ if (errors .Is (plumbing .ErrReferenceNotFound , err ) || matchRefNotFoundErr (err )) &&
96
+ ! strings .HasPrefix (params .Ref , "refs/" ) {
97
+ originalRefName := opts .ReferenceName
98
+ // If params.Ref is provided without refs/*, then we are assuming it to either refs/heads/ or refs/tags.
99
+ // Try clone again with inverse ref.
100
+ if opts .ReferenceName .IsBranch () {
101
+ opts .ReferenceName = plumbing .ReferenceName ("refs/tags/" + params .Ref )
102
+ } else if opts .ReferenceName .IsTag () {
103
+ opts .ReferenceName = plumbing .ReferenceName ("refs/heads/" + params .Ref )
104
+ } else {
105
+ return err // Return err if the reference name is invalid
106
+ }
107
+
108
+ r , err = git .PlainClone (params .Dir , false , opts )
109
+ if err == nil {
110
+ return nil
111
+ }
112
+ // Change reference name back to original
113
+ opts .ReferenceName = originalRefName
114
+ }
115
+ return err
116
+ }, b )
117
+
118
+ // If error not nil, then return it
119
+ if err != nil {
89
120
return err
90
121
}
91
122
0 commit comments