Skip to content

Commit f600e2a

Browse files
authored
Merge pull request #56 from bitrise-io/skip-existing-files
2 parents f4899d5 + 58407a5 commit f600e2a

File tree

4 files changed

+44
-26
lines changed

4 files changed

+44
-26
lines changed

.golangci.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,3 +29,4 @@ linters:
2929
- gci
3030
- nolintlint
3131
- cyclop
32+
- execinquery # deprecated (since v1.58.0) due to: The repository of the linter has been archived by the owner.

cmd/restoreXcodeDerivedDataFiles.go

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,12 @@ var restoreXcodeDerivedDataFilesCmd = &cobra.Command{
4747
projectRoot, _ := cmd.Flags().GetString("project-root")
4848
cacheKey, _ := cmd.Flags().GetString("key")
4949
forceOverwrite, _ := cmd.Flags().GetBool("force-overwrite-files")
50+
skipExisting, _ := cmd.Flags().GetBool("skip-existing-files")
5051
maxLoggedErrors, _ := cmd.Flags().GetInt("max-logged-errors")
5152

53+
logger.Infof("(i) Skip existing files: %t", skipExisting)
54+
logger.Infof("(i) Force overwrite existing files: %t", forceOverwrite)
55+
5256
tracker := xcode.NewDefaultStepTracker("restore-xcode-build-cache", os.Getenv, logger)
5357
defer tracker.Wait()
5458
startT := time.Now()
@@ -60,7 +64,7 @@ var restoreXcodeDerivedDataFilesCmd = &cobra.Command{
6064
}
6165

6266
op, cmdError := restoreXcodeDerivedDataFilesCmdFn(cmd.Context(), authConfig, CacheMetadataPath, projectRoot,
63-
cacheKey, logger, tracker, startT, os.Getenv, isDebugLogMode, forceOverwrite, maxLoggedErrors)
67+
cacheKey, logger, tracker, startT, os.Getenv, isDebugLogMode, skipExisting, forceOverwrite, maxLoggedErrors)
6468
if op != nil {
6569
if cmdError != nil {
6670
errStr := cmdError.Error()
@@ -92,11 +96,12 @@ func init() {
9296
panic(err)
9397
}
9498
restoreXcodeDerivedDataFilesCmd.Flags().Bool("force-overwrite-files", false, "If set, the command will try to overwrite existing files during restoring the cache even if the permissions do not allow it")
99+
restoreXcodeDerivedDataFilesCmd.Flags().Bool("skip-existing-files", false, "If set, existing files will be skipped and not be overwritten during restoring the cache")
95100
restoreXcodeDerivedDataFilesCmd.Flags().Int("max-logged-errors", 150, "The maximum number of errors logged to the console during restoring the cache.")
96101
}
97102

98103
func restoreXcodeDerivedDataFilesCmdFn(ctx context.Context, authConfig common.CacheAuthConfig, cacheMetadataPath, projectRoot, providedCacheKey string, logger log.Logger,
99-
tracker xcode.StepAnalyticsTracker, startT time.Time, envProvider func(string) string, isDebugLogMode, forceOverwrite bool, maxLoggedDownloadErrors int) (*xa.CacheOperation, error) {
104+
tracker xcode.StepAnalyticsTracker, startT time.Time, envProvider func(string) string, isDebugLogMode, skipExisting, forceOverwrite bool, maxLoggedDownloadErrors int) (*xa.CacheOperation, error) {
100105
op := newCacheOperation(startT, xa.OperationTypeDownload, envProvider)
101106
kvClient, err := createKVClient(ctx, op.OperationID, authConfig, envProvider, logger)
102107
if err != nil {
@@ -137,7 +142,7 @@ func restoreXcodeDerivedDataFilesCmdFn(ctx context.Context, authConfig common.Ca
137142
tracker.LogMetadataLoaded(metadataRestoredT.Sub(startT), string(cacheKeyType), len(metadata.ProjectFiles.Files)+len(metadata.ProjectFiles.Directories), filesUpdated, metadataSize)
138143

139144
logger.TInfof("Downloading DerivedData files")
140-
stats, err := xcode.DownloadCacheFilesFromBuildCache(ctx, metadata.DerivedData, kvClient, logger, isDebugLogMode, forceOverwrite, maxLoggedDownloadErrors)
145+
stats, err := xcode.DownloadCacheFilesFromBuildCache(ctx, metadata.DerivedData, kvClient, logger, isDebugLogMode, skipExisting, forceOverwrite, maxLoggedDownloadErrors)
141146
ddDownloadedT := time.Now()
142147
tracker.LogDerivedDataDownloaded(ddDownloadedT.Sub(metadataRestoredT), stats)
143148
fillCacheOperationWithDownloadStats(op, stats)
@@ -170,7 +175,7 @@ func restoreXcodeDerivedDataFilesCmdFn(ctx context.Context, authConfig common.Ca
170175

171176
if len(metadata.XcodeCacheDir.Files) > 0 {
172177
logger.TInfof("Downloading Xcode cache files")
173-
if _, err := xcode.DownloadCacheFilesFromBuildCache(ctx, metadata.XcodeCacheDir, kvClient, logger, isDebugLogMode, forceOverwrite, maxLoggedDownloadErrors); err != nil {
178+
if _, err := xcode.DownloadCacheFilesFromBuildCache(ctx, metadata.XcodeCacheDir, kvClient, logger, isDebugLogMode, skipExisting, forceOverwrite, maxLoggedDownloadErrors); err != nil {
174179
return op, fmt.Errorf("download Xcode cache files: %w", err)
175180
}
176181

internal/xcode/download.go

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,9 @@ var ErrFileExistsAndNotWritable = errors.New("file already exists and is not wri
2525
func DownloadFileFromBuildCache(ctx context.Context, fileName, key string, kvClient *kv.Client, logger log.Logger) error {
2626
logger.Debugf("Downloading %s", fileName)
2727

28-
return downloadFile(ctx, kvClient, fileName, key, 0, logger, false, false)
28+
_, err := downloadFile(ctx, kvClient, fileName, key, 0, logger, false, false, false)
29+
30+
return err
2931
}
3032

3133
func DownloadStreamFromBuildCache(ctx context.Context, destination io.Writer, key string, kvClient *kv.Client, logger log.Logger) error {
@@ -35,29 +37,33 @@ func DownloadStreamFromBuildCache(ctx context.Context, destination io.Writer, ke
3537
}
3638

3739
// nolint: nestif
38-
func downloadFile(ctx context.Context, client *kv.Client, filePath, key string, fileMode os.FileMode, logger log.Logger, isDebugLogMode, forceOverwrite bool) error {
40+
func downloadFile(ctx context.Context, client *kv.Client, filePath, key string, fileMode os.FileMode, logger log.Logger, isDebugLogMode, skipExisting, forceOverwrite bool) (bool, error) {
3941
dir := filepath.Dir(filePath)
4042
if err := os.MkdirAll(dir, os.ModePerm); err != nil {
41-
return fmt.Errorf("create directory: %w", err)
43+
return false, fmt.Errorf("create directory: %w", err)
4244
}
4345

4446
if fileMode == 0 {
4547
fileMode = 0666
4648
}
4749

4850
if fileInfo, err := os.Stat(filePath); err == nil {
51+
if skipExisting {
52+
return true, nil
53+
}
54+
4955
ownerWritable := (fileInfo.Mode().Perm() & 0200) != 0
5056
if !ownerWritable {
5157
if !forceOverwrite {
52-
return ErrFileExistsAndNotWritable
58+
return false, ErrFileExistsAndNotWritable
5359
}
5460

5561
if err := os.Chmod(filePath, 0666); err != nil {
56-
return fmt.Errorf("force overwrite - failed to change existing file permissions: %w", err)
62+
return false, fmt.Errorf("force overwrite - failed to change existing file permissions: %w", err)
5763
}
5864

5965
if err := os.Remove(filePath); err != nil {
60-
return fmt.Errorf("force overwrite - failed to remove existing file: %w", err)
66+
return false, fmt.Errorf("force overwrite - failed to remove existing file: %w", err)
6167
}
6268
}
6369
}
@@ -68,11 +74,11 @@ func downloadFile(ctx context.Context, client *kv.Client, filePath, key string,
6874
logFilePathDebugInfo(filePath, logger)
6975
}
7076

71-
return fmt.Errorf("create %q: %w", filePath, err)
77+
return false, fmt.Errorf("create %q: %w", filePath, err)
7278
}
7379
defer file.Close()
7480

75-
return downloadStream(ctx, file, client, key)
81+
return false, downloadStream(ctx, file, client, key)
7682
}
7783

7884
func downloadStream(ctx context.Context, destination io.Writer, client *kv.Client, key string) error {

internal/xcode/download_multi.go

Lines changed: 20 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ type DownloadFilesStats struct {
2929

3030
// nolint: gocognit
3131
func DownloadCacheFilesFromBuildCache(ctx context.Context, dd FileGroupInfo, kvClient *kv.Client, logger log.Logger,
32-
isDebugLogMode, forceOverwrite bool, maxLoggedDownloadErrors int) (DownloadFilesStats, error) {
32+
isDebugLogMode, skipExisting, forceOverwrite bool, maxLoggedDownloadErrors int) (DownloadFilesStats, error) {
3333
var largestFileSize int64
3434
for _, file := range dd.Files {
3535
if file.Size > largestFileSize {
@@ -44,6 +44,7 @@ func DownloadCacheFilesFromBuildCache(ctx context.Context, dd FileGroupInfo, kvC
4444
var filesMissing atomic.Int32
4545
var filesFailedToDownload atomic.Int32
4646
var downloadSize atomic.Int64
47+
var skippedFiles atomic.Int32
4748

4849
var wg sync.WaitGroup
4950
semaphore := make(chan struct{}, 20) // Limit parallelization
@@ -57,7 +58,13 @@ func DownloadCacheFilesFromBuildCache(ctx context.Context, dd FileGroupInfo, kvC
5758

5859
const retries = 3
5960
err := retry.Times(retries).Wait(3 * time.Second).TryWithAbort(func(_ uint) (error, bool) {
60-
err := downloadFile(ctx, kvClient, file.Path, file.Hash, file.Mode, logger, isDebugLogMode, forceOverwrite)
61+
skipped, err := downloadFile(ctx, kvClient, file.Path, file.Hash, file.Mode, logger, isDebugLogMode, forceOverwrite, skipExisting)
62+
if skipped {
63+
skippedFiles.Add(1)
64+
65+
return nil, false
66+
}
67+
6168
if errors.Is(err, ErrCacheNotFound) {
6269
return err, true
6370
} else if errors.Is(err, ErrFileExistsAndNotWritable) {
@@ -100,28 +107,27 @@ func DownloadCacheFilesFromBuildCache(ctx context.Context, dd FileGroupInfo, kvC
100107
logger.TInfof("(i) Downloaded: %d files (%s). Missing: %d files. Failed: %d files", filesDownloaded.Load(), humanize.Bytes(uint64(downloadSize.Load())), filesMissing.Load(), filesFailedToDownload.Load())
101108

102109
stats := DownloadFilesStats{
103-
FilesToBeDownloaded: len(dd.Files),
110+
FilesToBeDownloaded: len(dd.Files) - int(skippedFiles.Load()),
104111
FilesDownloaded: int(filesDownloaded.Load()),
105112
FilesMissing: int(filesMissing.Load()),
106113
FilesFailedToDownload: int(filesFailedToDownload.Load()),
107114
DownloadSize: downloadSize.Load(),
108115
LargestFileSize: largestFileSize,
109116
}
110117
logger.Debugf("Download stats:")
111-
logger.Debugf(" Files to be downloaded: %d", len(dd.Files))
112-
logger.Debugf(" Files downloaded: %d", int(filesDownloaded.Load()))
113-
logger.Debugf(" Files missing: %d", int(filesMissing.Load()))
114-
logger.Debugf(" Files failed to download: %d", int(filesFailedToDownload.Load()))
115-
logger.Debugf(" Download size: %s", humanize.Bytes(uint64(downloadSize.Load())))
116-
logger.Debugf(" Largest file size: %s", humanize.Bytes(uint64(largestFileSize)))
117-
118-
downloadErrors := int(filesFailedToDownload.Load())
119-
missingFiles := int(filesMissing.Load())
120-
if maxLoggedDownloadErrors < downloadErrors+missingFiles {
118+
logger.Debugf(" Files to be downloaded: %d", stats.FilesToBeDownloaded)
119+
logger.Debugf(" Files downloaded: %d", stats.FilesDownloaded)
120+
logger.Debugf(" Files missing: %d", stats.FilesMissing)
121+
logger.Debugf(" Files failed to download: %d", stats.FilesFailedToDownload)
122+
logger.Debugf(" Files skipped (existing): %d", skippedFiles.Load())
123+
logger.Debugf(" Download size: %s", humanize.Bytes(uint64(stats.DownloadSize)))
124+
logger.Debugf(" Largest file size: %s", humanize.Bytes(uint64(stats.LargestFileSize)))
125+
126+
if maxLoggedDownloadErrors < stats.FilesFailedToDownload+stats.FilesMissing {
121127
logger.Warnf("Too many download errors or missing files, only the first %d errors were logged", maxLoggedDownloadErrors)
122128
}
123129

124-
if filesFailedToDownload.Load() > 0 || filesMissing.Load() > 0 {
130+
if stats.FilesFailedToDownload > 0 || stats.FilesMissing > 0 {
125131
return stats, fmt.Errorf("failed to download some files")
126132
}
127133

0 commit comments

Comments
 (0)