@@ -90,9 +90,12 @@ func (rs *GSUtilCache) ExistingPackages(ctx context.Context, pkgs []cache.Packag
9090 return existingPackages , nil
9191}
9292
93- // Download makes a best-effort attempt at downloading previously cached build artifacts
94- func (rs * GSUtilCache ) Download (ctx context.Context , dst cache.LocalCache , pkgs []cache.Package ) error {
93+ // Download makes a best-effort attempt at downloading previously cached build artifacts.
94+ // Returns detailed results for each package to enable smarter retry decisions.
95+ func (rs * GSUtilCache ) Download (ctx context.Context , dst cache.LocalCache , pkgs []cache.Package ) map [string ]cache.DownloadResult {
96+ results := make (map [string ]cache.DownloadResult )
9597 fmt .Printf ("☁️ downloading %d cached build artifacts\n " , len (pkgs ))
98+
9699 var (
97100 files []string
98101 dest string
@@ -108,17 +111,21 @@ func (rs *GSUtilCache) Download(ctx context.Context, dst cache.LocalCache, pkgs
108111 for _ , pkg := range pkgs {
109112 fn , exists := dst .Location (pkg )
110113 if exists {
114+ results [pkg .FullName ()] = cache.DownloadResult {Status : cache .DownloadStatusSkipped }
111115 continue
112116 }
113117 version , err := pkg .Version ()
114118 if err != nil {
115119 log .WithError (err ).WithField ("package" , pkg .FullName ()).Warn ("Failed to get version for package, skipping" )
120+ results [pkg .FullName ()] = cache.DownloadResult {Status : cache .DownloadStatusFailed , Err : err }
116121 continue
117122 }
118123 if dest == "" {
119124 dest = filepath .Dir (fn )
120125 } else if dest != filepath .Dir (fn ) {
121- return fmt .Errorf ("gsutil only supports one target folder, not %s and %s" , dest , filepath .Dir (fn ))
126+ err := fmt .Errorf ("gsutil only supports one target folder, not %s and %s" , dest , filepath .Dir (fn ))
127+ results [pkg .FullName ()] = cache.DownloadResult {Status : cache .DownloadStatusFailed , Err : err }
128+ continue
122129 }
123130
124131 pair := urlPair {
@@ -129,7 +136,7 @@ func (rs *GSUtilCache) Download(ctx context.Context, dst cache.LocalCache, pkgs
129136 urls = append (urls , pair .gzURL , pair .tarURL )
130137 }
131138 if len (urls ) == 0 {
132- return nil
139+ return results
133140 }
134141
135142 args := append ([]string {"stat" }, urls ... )
@@ -142,21 +149,49 @@ func (rs *GSUtilCache) Download(ctx context.Context, dst cache.LocalCache, pkgs
142149 err := cmd .Run ()
143150 if err != nil && (! strings .Contains (stderrBuffer .String (), "No URLs matched" )) {
144151 log .Debugf ("gsutil stat returned non-zero exit code: [%v], stderr: [%v]" , err , stderrBuffer .String ())
145- return fmt .Errorf ("failed to check if files exist in remote cache: %w" , err )
152+ // Mark all pending packages as failed
153+ for pkg := range packageToURLMap {
154+ if _ , exists := results [pkg .FullName ()]; ! exists {
155+ results [pkg .FullName ()] = cache.DownloadResult {Status : cache .DownloadStatusFailed , Err : err }
156+ }
157+ }
158+ return results
146159 }
147160
148161 existingURLs := parseGSUtilStatOutput (strings .NewReader (stdoutBuffer .String ()))
149- for _ , urls := range packageToURLMap {
162+ packagesToDownload := make (map [cache.Package ]bool )
163+ for pkg , urls := range packageToURLMap {
150164 if _ , exists := existingURLs [urls .gzURL ]; exists {
151165 files = append (files , urls .gzURL )
166+ packagesToDownload [pkg ] = true
152167 continue
153168 }
154169 if _ , exists := existingURLs [urls .tarURL ]; exists {
155170 files = append (files , urls .tarURL )
171+ packagesToDownload [pkg ] = true
172+ continue
173+ }
174+ // Package not found in remote cache
175+ results [pkg .FullName ()] = cache.DownloadResult {Status : cache .DownloadStatusNotFound }
176+ }
177+
178+ if len (files ) > 0 {
179+ transferErr := gsutilTransfer (dest , files )
180+ for pkg := range packagesToDownload {
181+ if transferErr != nil {
182+ results [pkg .FullName ()] = cache.DownloadResult {Status : cache .DownloadStatusFailed , Err : transferErr }
183+ } else {
184+ // Verify the file was actually downloaded
185+ if _ , exists := dst .Location (pkg ); exists {
186+ results [pkg .FullName ()] = cache.DownloadResult {Status : cache .DownloadStatusSuccess }
187+ } else {
188+ results [pkg .FullName ()] = cache.DownloadResult {Status : cache .DownloadStatusFailed , Err : fmt .Errorf ("file not found after transfer" )}
189+ }
190+ }
156191 }
157192 }
158193
159- return gsutilTransfer ( dest , files )
194+ return results
160195}
161196
162197// Upload makes a best effort to upload the build artifacts to a remote cache
@@ -195,7 +230,7 @@ func (rs *GSUtilCache) UploadFile(ctx context.Context, filePath string, key stri
195230// HasFile checks if a file exists in the remote cache with the given key
196231func (rs * GSUtilCache ) HasFile (ctx context.Context , key string ) (bool , error ) {
197232 target := fmt .Sprintf ("gs://%s/%s" , rs .BucketName , key )
198-
233+
199234 cmd := exec .CommandContext (ctx , "gsutil" , "stat" , target )
200235 if err := cmd .Run (); err != nil {
201236 // gsutil stat returns non-zero exit code if file doesn't exist
@@ -209,7 +244,7 @@ func (rs *GSUtilCache) HasFile(ctx context.Context, key string) (bool, error) {
209244 }
210245 return false , fmt .Errorf ("failed to check if file exists at %s: %w" , target , err )
211246 }
212-
247+
213248 return true , nil
214249}
215250
0 commit comments