99	"slices" 
1010	"strings" 
1111	"sync" 
12+ 	"syscall" 
1213	"time" 
1314
1415	"github.com/go-git/go-billy/v6/osfs" 
@@ -33,19 +34,20 @@ type Repository struct {
3334
3435	logger  * zap.Logger 
3536
36- 	mu                  sync.RWMutex 
37- 	remote              * config.RemoteConfig 
38- 	defaultBranch       string 
39- 	auth                transport.AuthMethod 
40- 	insecureSkipTLS     bool 
41- 	caBundle            []byte 
42- 	localPath           string 
43- 	readme              []byte 
44- 	sigName             string 
45- 	sigEmail            string 
46- 	signer              signing.Signer 
47- 	maxOpenDescriptors  int 
48- 	isNormalRepo        bool  // true if opened with PlainOpen, false if bare repository 
37+ 	mu                        sync.RWMutex 
38+ 	remote                    * config.RemoteConfig 
39+ 	remoteStartupFetchPolicy  string 
40+ 	defaultBranch             string 
41+ 	auth                      transport.AuthMethod 
42+ 	insecureSkipTLS           bool 
43+ 	caBundle                  []byte 
44+ 	localPath                 string 
45+ 	readme                    []byte 
46+ 	sigName                   string 
47+ 	sigEmail                  string 
48+ 	signer                    signing.Signer 
49+ 	maxOpenDescriptors        int 
50+ 	isNormalRepo              bool  // true if opened with PlainOpen, false if bare repository 
4951
5052	subs  []Subscriber 
5153
@@ -71,7 +73,7 @@ func NewRepository(ctx context.Context, logger *zap.Logger, opts ...containers.O
7173		logger .Debug ("repository empty, attempting to add and push a README" )
7274		// add initial readme if repo is empty 
7375		if  _ , err  :=  repo .UpdateAndPush (ctx , repo .defaultBranch , func (fs  envsfs.Filesystem ) (string , error ) {
74- 			fi , err  :=  fs .OpenFile ("README.md" , os .O_CREATE | os .O_RDWR | os .O_TRUNC , 0644 )
76+ 			fi , err  :=  fs .OpenFile ("README.md" , os .O_CREATE | os .O_RDWR | os .O_TRUNC , 0o644 )
7577			if  err  !=  nil  {
7678				return  "" , err 
7779			}
@@ -198,15 +200,28 @@ func newRepository(ctx context.Context, logger *zap.Logger, opts ...containers.O
198200
199201		// do an initial fetch to setup remote tracking branches 
200202		if  err  :=  r .Fetch (ctx ); err  !=  nil  {
201- 			if  ! errors .Is (err , transport .ErrEmptyRemoteRepository ) &&  ! errors .Is (err , git .ErrRemoteRefNotFound ) {
202- 				return  nil , empty , fmt .Errorf ("performing initial fetch: %w" , err )
203+ 			fetchErr  :=  fmt .Errorf ("performing initial fetch: %w" , err )
204+ 			switch  {
205+ 			case  r .remoteStartupFetchPolicy  ==  "optional"  &&  r .IsConnectionRefused (err ):
206+ 				// if optional, we check if the error is connection refused 
207+ 				// and there is non-empty repo and flags could be evaluated 
208+ 				objs , rerr  :=  r .CommitObjects ()
209+ 				if  rerr  !=  nil  {
210+ 					return  nil , empty , fetchErr 
211+ 				}
212+ 				_ , rerr  =  objs .Next ()
213+ 				objs .Close ()
214+ 				if  rerr  !=  nil  {
215+ 					return  nil , empty , fetchErr 
216+ 				}
217+ 			case  errors .Is (err , transport .ErrEmptyRemoteRepository ) ||  errors .Is (err , git .ErrRemoteRefNotFound ):
218+ 				// the remote was reachable but either its contents was completely empty 
219+ 				// or our default branch doesn't exist and so we decide to seed it 
220+ 				empty  =  true 
221+ 				logger .Debug ("initial fetch empty" , zap .String ("reference" , r .defaultBranch ), zap .Error (err ))
222+ 			default :
223+ 				return  nil , empty , fetchErr 
203224			}
204- 
205- 			// the remote was reachable but either its contents was completely empty 
206- 			// or our default branch doesn't exist and so we decide to seed it 
207- 			empty  =  true 
208- 
209- 			logger .Debug ("initial fetch empty" , zap .String ("reference" , r .defaultBranch ), zap .Error (err ))
210225		}
211226	}
212227
@@ -296,6 +311,17 @@ func (r *Repository) fetchHeads() []string {
296311	return  slices .Collect (maps .Keys (heads ))
297312}
298313
314+ // IsConnectionRefused checks if the provided error is a connection refused error. 
315+ func  (r  * Repository ) IsConnectionRefused (err  error ) bool  {
316+ 	return  errors .Is (err , syscall .ECONNREFUSED ) ||  errors .Is (err , syscall .EHOSTUNREACH ) || 
317+ 		errors .Is (err , syscall .ENETUNREACH ) ||  errors .Is (err , syscall .EHOSTDOWN )
318+ }
319+ 
320+ // RemoteStartupFetchPolicy returns the fetch policy used when starting up with a remote configured. 
321+ func  (r  * Repository ) RemoteStartupFetchPolicy () string  {
322+ 	return  r .remoteStartupFetchPolicy 
323+ }
324+ 
299325// Fetch does a fetch for the requested head names on a configured remote. 
300326// If the remote is not defined, then it is a silent noop. 
301327// Iff specific is explicitly requested then only the heads in specific are fetched. 
@@ -830,6 +856,13 @@ func WithMaxOpenDescriptors(n int) containers.Option[Repository] {
830856	}
831857}
832858
859+ // withRemoteStartupFetchPolicy sets the fetch policy to use when starting up with a remote configured. 
860+ func  WithRemoteStartupFetchPolicy (policy  string ) containers.Option [Repository ] {
861+ 	return  func (r  * Repository ) {
862+ 		r .remoteStartupFetchPolicy  =  policy 
863+ 	}
864+ }
865+ 
833866const  defaultReadmeContents  =  `Flipt Configuration Repository 
834867============================== 
835868
0 commit comments