From 4d7177e69be4b74ee7fef97579272682363fc287 Mon Sep 17 00:00:00 2001 From: tomer doron Date: Wed, 15 Nov 2023 16:55:43 -0800 Subject: [PATCH 1/4] cache binary artifact globally motivation: like other dependencies, binary artifacts are good candidates for user level caching such that they do not need to be re-downloaded changes: * update BinaryArtifactsManager to take cache path and use the cache to store binary artifacts when downloading them * update test infra to enable/disable artifacts caching * update workspaace call sites * update workspace delegate to indicate when using cached binary artifact * update and add tests rdar://111774147 --- .../PackageTools/ComputeChecksum.swift | 1 + Sources/Commands/ToolWorkspaceDelegate.swift | 18 +- Sources/SPMTestSupport/MockWorkspace.swift | 4 +- Sources/SourceControl/RepositoryManager.swift | 2 +- .../Workspace/Workspace+BinaryArtifacts.swift | 161 ++++++++++--- .../Workspace/Workspace+Configuration.swift | 5 + Sources/Workspace/Workspace+Delegation.swift | 10 +- Sources/Workspace/Workspace.swift | 1 + Tests/WorkspaceTests/WorkspaceTests.swift | 228 +++++++++++++++++- 9 files changed, 375 insertions(+), 55 deletions(-) diff --git a/Sources/Commands/PackageTools/ComputeChecksum.swift b/Sources/Commands/PackageTools/ComputeChecksum.swift index 192cbaf1bf1..af22f838671 100644 --- a/Sources/Commands/PackageTools/ComputeChecksum.swift +++ b/Sources/Commands/PackageTools/ComputeChecksum.swift @@ -33,6 +33,7 @@ struct ComputeChecksum: SwiftCommand { authorizationProvider: swiftTool.getAuthorizationProvider(), hostToolchain: swiftTool.getHostToolchain(), checksumAlgorithm: SHA256(), + cachePath: .none, customHTTPClient: .none, customArchiver: .none, delegate: .none diff --git a/Sources/Commands/ToolWorkspaceDelegate.swift b/Sources/Commands/ToolWorkspaceDelegate.swift index 37ccf8b4c98..1c263106073 100644 --- a/Sources/Commands/ToolWorkspaceDelegate.swift +++ b/Sources/Commands/ToolWorkspaceDelegate.swift @@ -135,12 +135,16 @@ class ToolWorkspaceDelegate: WorkspaceDelegate { self.outputHandler("Computed \(location) at \(version) (\(duration.descriptionInSeconds))", false) } - func willDownloadBinaryArtifact(from url: String) { - self.outputHandler("Downloading binary artifact \(url)", false) + func willDownloadBinaryArtifact(from url: String, fromCache: Bool) { + if fromCache { + self.outputHandler("Fetching binary artifact \(url) from cache", false) + } else { + self.outputHandler("Downloading binary artifact \(url)", false) + } } - func didDownloadBinaryArtifact(from url: String, result: Result, duration: DispatchTimeInterval) { - guard case .success = result, !self.observabilityScope.errorsReported else { + func didDownloadBinaryArtifact(from url: String, result: Result<(path: AbsolutePath, fromCache: Bool), Error>, duration: DispatchTimeInterval) { + guard case .success(let fetchDetails) = result, !self.observabilityScope.errorsReported else { return } @@ -155,7 +159,11 @@ class ToolWorkspaceDelegate: WorkspaceDelegate { } } - self.outputHandler("Downloaded \(url) (\(duration.descriptionInSeconds))", false) + if fetchDetails.fromCache { + self.outputHandler("Fetched \(url) (\(duration.descriptionInSeconds))", false) + } else { + self.outputHandler("Downloaded \(url) (\(duration.descriptionInSeconds))", false) + } } func downloadingBinaryArtifact(from url: String, bytesDownloaded: Int64, totalBytesToDownload: Int64?) { diff --git a/Sources/SPMTestSupport/MockWorkspace.swift b/Sources/SPMTestSupport/MockWorkspace.swift index 6cd5e4ca213..7070e6c441e 100644 --- a/Sources/SPMTestSupport/MockWorkspace.swift +++ b/Sources/SPMTestSupport/MockWorkspace.swift @@ -893,11 +893,11 @@ public final class MockWorkspaceDelegate: WorkspaceDelegate { // noop } - public func willDownloadBinaryArtifact(from url: String) { + public func willDownloadBinaryArtifact(from url: String, fromCache: Bool) { self.append("downloading binary artifact package: \(url)") } - public func didDownloadBinaryArtifact(from url: String, result: Result, duration: DispatchTimeInterval) { + public func didDownloadBinaryArtifact(from url: String, result: Result<(path: AbsolutePath, fromCache: Bool), Error>, duration: DispatchTimeInterval) { self.append("finished downloading binary artifact package: \(url)") } diff --git a/Sources/SourceControl/RepositoryManager.swift b/Sources/SourceControl/RepositoryManager.swift index fca368c33c8..3b6dc1f2b16 100644 --- a/Sources/SourceControl/RepositoryManager.swift +++ b/Sources/SourceControl/RepositoryManager.swift @@ -441,7 +441,7 @@ public class RepositoryManager: Cancellable { } /// Sets up the cache directories if they don't already exist. - public func initializeCacheIfNeeded(cachePath: AbsolutePath) throws { + private func initializeCacheIfNeeded(cachePath: AbsolutePath) throws { // Create the supplied cache directory. if !self.fileSystem.exists(cachePath) { try self.fileSystem.createDirectory(cachePath, recursive: true) diff --git a/Sources/Workspace/Workspace+BinaryArtifacts.swift b/Sources/Workspace/Workspace+BinaryArtifacts.swift index 0ff9da36ee6..7fee94f3230 100644 --- a/Sources/Workspace/Workspace+BinaryArtifacts.swift +++ b/Sources/Workspace/Workspace+BinaryArtifacts.swift @@ -26,10 +26,16 @@ extension Workspace { public struct CustomBinaryArtifactsManager { let httpClient: LegacyHTTPClient? let archiver: Archiver? + let useCache: Bool? - public init(httpClient: LegacyHTTPClient? = .none, archiver: Archiver? = .none) { + public init( + httpClient: LegacyHTTPClient? = .none, + archiver: Archiver? = .none, + useCache: Bool? = .none + ) { self.httpClient = httpClient self.archiver = archiver + self.useCache = useCache } } @@ -43,6 +49,7 @@ extension Workspace { private let httpClient: LegacyHTTPClient private let archiver: Archiver private let checksumAlgorithm: HashAlgorithm + private let cachePath: AbsolutePath? private let delegate: Delegate? public init( @@ -50,6 +57,7 @@ extension Workspace { authorizationProvider: AuthorizationProvider?, hostToolchain: UserToolchain, checksumAlgorithm: HashAlgorithm, + cachePath: AbsolutePath?, customHTTPClient: LegacyHTTPClient?, customArchiver: Archiver?, delegate: Delegate? @@ -60,6 +68,7 @@ extension Workspace { self.checksumAlgorithm = checksumAlgorithm self.httpClient = customHTTPClient ?? LegacyHTTPClient() self.archiver = customArchiver ?? ZipArchiver(fileSystem: fileSystem) + self.cachePath = cachePath self.delegate = delegate } @@ -126,7 +135,7 @@ extension Workspace { return (local: localArtifacts, remote: remoteArtifacts) } - func download( + func fetch( _ artifacts: [RemoteArtifact], artifactsDirectory: AbsolutePath, observabilityScope: ObservabilityScope @@ -229,23 +238,11 @@ extension Workspace { } group.enter() - var headers = HTTPClientHeaders() - headers.add(name: "Accept", value: "application/octet-stream") - var request = LegacyHTTPClient.Request.download( - url: artifact.url, - headers: headers, - fileSystem: self.fileSystem, - destination: archivePath - ) - request.options.authorizationProvider = self.authorizationProvider?.httpAuthorizationHeader(for:) - request.options.retryStrategy = .exponentialBackoff(maxAttempts: 3, baseDelay: .milliseconds(50)) - request.options.validResponseCodes = [200] - - let downloadStart: DispatchTime = .now() - self.delegate?.willDownloadBinaryArtifact(from: artifact.url.absoluteString) - observabilityScope.emit(debug: "downloading \(artifact.url) to \(archivePath)") - self.httpClient.execute( - request, + let fetchStart: DispatchTime = .now() + self.fetch( + artifact: artifact, + destination: archivePath, + observabilityScope: observabilityScope, progress: { bytesDownloaded, totalBytesToDownload in self.delegate?.downloadingBinaryArtifact( from: artifact.url.absoluteString, @@ -253,13 +250,12 @@ extension Workspace { totalBytesToDownload: totalBytesToDownload ) }, - completion: { downloadResult in + completion: { fetchResult in defer { group.leave() } - // TODO: Use the same extraction logic for both remote and local archived artifacts. - switch downloadResult { - case .success: - + switch fetchResult { + case .success(let cached): + // TODO: Use the same extraction logic for both remote and local archived artifacts. group.enter() observabilityScope.emit(debug: "validating \(archivePath)") self.archiver.validate(path: archivePath, completion: { validationResult in @@ -381,8 +377,8 @@ extension Workspace { ) self.delegate?.didDownloadBinaryArtifact( from: artifact.url.absoluteString, - result: .success(artifactPath), - duration: downloadStart.distance(to: .now()) + result: .success((path: artifactPath, fromCache: cached)), + duration: fetchStart.distance(to: .now()) ) case .failure(let error): observabilityScope.emit(.remoteArtifactFailedExtraction( @@ -393,7 +389,7 @@ extension Workspace { self.delegate?.didDownloadBinaryArtifact( from: artifact.url.absoluteString, result: .failure(error), - duration: downloadStart.distance(to: .now()) + duration: fetchStart.distance(to: .now()) ) } @@ -409,7 +405,7 @@ extension Workspace { self.delegate?.didDownloadBinaryArtifact( from: artifact.url.absoluteString, result: .failure(error), - duration: downloadStart.distance(to: .now()) + duration: fetchStart.distance(to: .now()) ) } }) @@ -423,7 +419,7 @@ extension Workspace { self.delegate?.didDownloadBinaryArtifact( from: artifact.url.absoluteString, result: .failure(error), - duration: downloadStart.distance(to: .now()) + duration: fetchStart.distance(to: .now()) ) } } @@ -563,23 +559,124 @@ extension Workspace { try cancellableArchiver.cancel(deadline: deadline) } } + + private func fetch( + artifact: RemoteArtifact, + destination: AbsolutePath, + observabilityScope: ObservabilityScope, + progress: @escaping (Int64, Optional) -> Void, + completion: @escaping (Result) -> Void + ) { + // not using cache, download directly + guard let cachePath = self.cachePath else { + self.delegate?.willDownloadBinaryArtifact(from: artifact.url.absoluteString, fromCache: false) + return self.download( + artifact: artifact, + destination: destination, + observabilityScope: observabilityScope, + progress: progress, + completion: { result in + // not fetched from cache + completion(result.map{ _ in false }) + } + ) + } + + // initialize cache if necessary + do { + if !self.fileSystem.exists(cachePath) { + try self.fileSystem.createDirectory(cachePath, recursive: true) + } + } catch { + return completion(.failure(error)) + } + + + // try to fetch from cache, or download and cache + // / FIXME: use better escaping of URL + let cacheKey = artifact.url.absoluteString.spm_mangledToC99ExtendedIdentifier() + let cachedArtifactPath = cachePath.appending(cacheKey) + + if self.fileSystem.exists(cachedArtifactPath) { + observabilityScope.emit(debug: "copying cached binary artifact for \(artifact.url) from \(cachedArtifactPath)") + self.delegate?.willDownloadBinaryArtifact(from: artifact.url.absoluteString, fromCache: true) + return completion( + Result.init(catching: { + // copy from cache to destination + try self.fileSystem.copy(from: cachedArtifactPath, to: destination) + return true // fetched from cache + }) + ) + } + + // download to the cache + observabilityScope.emit(debug: "downloading binary artifact for \(artifact.url) to cached at \(cachedArtifactPath)") + self.download( + artifact: artifact, + destination: cachedArtifactPath, + observabilityScope: observabilityScope, + progress: progress, + completion: { result in + self.delegate?.willDownloadBinaryArtifact(from: artifact.url.absoluteString, fromCache: false) + completion(result.flatMap { + Result.init(catching: { + // copy from cache to destination + try self.fileSystem.copy(from: cachedArtifactPath, to: destination) + return false // not fetched from cache + }) + }) + } + ) + } + + private func download( + artifact: RemoteArtifact, + destination: AbsolutePath, + observabilityScope: ObservabilityScope, + progress: @escaping (Int64, Optional) -> Void, + completion: @escaping (Result) -> Void + ) { + observabilityScope.emit(debug: "downloading \(artifact.url) to \(destination)") + + var headers = HTTPClientHeaders() + headers.add(name: "Accept", value: "application/octet-stream") + var request = LegacyHTTPClient.Request.download( + url: artifact.url, + headers: headers, + fileSystem: self.fileSystem, + destination: destination + ) + request.options.authorizationProvider = self.authorizationProvider?.httpAuthorizationHeader(for:) + request.options.retryStrategy = .exponentialBackoff(maxAttempts: 3, baseDelay: .milliseconds(50)) + request.options.validResponseCodes = [200] + + self.httpClient.execute( + request, + progress: progress, + completion: { result in + completion(result.map{ _ in Void() }) + } + ) + } } } /// Delegate to notify clients about actions being performed by BinaryArtifactsDownloadsManage. public protocol BinaryArtifactsManagerDelegate { /// The workspace has started downloading a binary artifact. - func willDownloadBinaryArtifact(from url: String) + func willDownloadBinaryArtifact(from url: String, fromCache: Bool) /// The workspace has finished downloading a binary artifact. func didDownloadBinaryArtifact( from url: String, - result: Result, + result: Result<(path: AbsolutePath, fromCache: Bool), Error>, duration: DispatchTimeInterval ) /// The workspace is downloading a binary artifact. func downloadingBinaryArtifact(from url: String, bytesDownloaded: Int64, totalBytesToDownload: Int64?) /// The workspace finished downloading all binary artifacts. func didDownloadAllBinaryArtifacts() + + } extension Workspace.BinaryArtifactsManager { @@ -833,7 +930,7 @@ extension Workspace { } // Download the artifacts - let downloadedArtifacts = try self.binaryArtifactsManager.download( + let downloadedArtifacts = try self.binaryArtifactsManager.fetch( artifactsToDownload, artifactsDirectory: self.location.artifactsDirectory, observabilityScope: observabilityScope diff --git a/Sources/Workspace/Workspace+Configuration.swift b/Sources/Workspace/Workspace+Configuration.swift index ce3cf9a17bb..103a0f13aeb 100644 --- a/Sources/Workspace/Workspace+Configuration.swift +++ b/Sources/Workspace/Workspace+Configuration.swift @@ -145,6 +145,11 @@ extension Workspace { self.sharedCacheDirectory.map { $0.appending(components: "registry", "downloads") } } + /// Path to the shared repositories cache. + public var sharedBinaryArtifactsCacheDirectory: AbsolutePath? { + self.sharedCacheDirectory.map { $0.appending("artifacts") } + } + /// Create a new workspace location. /// /// - Parameters: diff --git a/Sources/Workspace/Workspace+Delegation.swift b/Sources/Workspace/Workspace+Delegation.swift index 4d58798dd04..dac189887fd 100644 --- a/Sources/Workspace/Workspace+Delegation.swift +++ b/Sources/Workspace/Workspace+Delegation.swift @@ -126,11 +126,11 @@ public protocol WorkspaceDelegate: AnyObject { func resolvedFileChanged() /// The workspace has started downloading a binary artifact. - func willDownloadBinaryArtifact(from url: String) + func willDownloadBinaryArtifact(from url: String, fromCache: Bool) /// The workspace has finished downloading a binary artifact. func didDownloadBinaryArtifact( from url: String, - result: Result, + result: Result<(path: AbsolutePath, fromCache: Bool), Error>, duration: DispatchTimeInterval ) /// The workspace is downloading a binary artifact. @@ -399,13 +399,13 @@ struct WorkspaceBinaryArtifactsManagerDelegate: Workspace.BinaryArtifactsManager self.workspaceDelegate = workspaceDelegate } - func willDownloadBinaryArtifact(from url: String) { - self.workspaceDelegate?.willDownloadBinaryArtifact(from: url) + func willDownloadBinaryArtifact(from url: String, fromCache: Bool) { + self.workspaceDelegate?.willDownloadBinaryArtifact(from: url, fromCache: fromCache) } func didDownloadBinaryArtifact( from url: String, - result: Result, + result: Result<(path: AbsolutePath, fromCache: Bool), Error>, duration: DispatchTimeInterval ) { self.workspaceDelegate?.didDownloadBinaryArtifact(from: url, result: result, duration: duration) diff --git a/Sources/Workspace/Workspace.swift b/Sources/Workspace/Workspace.swift index 48ad9351a7b..d8f6a9ceb44 100644 --- a/Sources/Workspace/Workspace.swift +++ b/Sources/Workspace/Workspace.swift @@ -528,6 +528,7 @@ public class Workspace { authorizationProvider: authorizationProvider, hostToolchain: hostToolchain, checksumAlgorithm: checksumAlgorithm, + cachePath: customBinaryArtifactsManager?.useCache == false || !configuration.sharedDependenciesCacheEnabled ? .none : location.sharedBinaryArtifactsCacheDirectory, customHTTPClient: customBinaryArtifactsManager?.httpClient, customArchiver: customBinaryArtifactsManager?.archiver, delegate: delegate.map(WorkspaceBinaryArtifactsManagerDelegate.init(workspaceDelegate:)) diff --git a/Tests/WorkspaceTests/WorkspaceTests.swift b/Tests/WorkspaceTests/WorkspaceTests.swift index 5613b08f715..41bff99c138 100644 --- a/Tests/WorkspaceTests/WorkspaceTests.swift +++ b/Tests/WorkspaceTests/WorkspaceTests.swift @@ -6880,7 +6880,8 @@ final class WorkspaceTests: XCTestCase { ], binaryArtifactsManager: .init( httpClient: httpClient, - archiver: archiver + archiver: archiver, + useCache: false // disable cache ) ) @@ -7103,7 +7104,8 @@ final class WorkspaceTests: XCTestCase { ], binaryArtifactsManager: .init( httpClient: httpClient, - archiver: archiver + archiver: archiver, + useCache: false ) ) @@ -7338,7 +7340,8 @@ final class WorkspaceTests: XCTestCase { ], binaryArtifactsManager: .init( httpClient: httpClient, - archiver: archiver + archiver: archiver, + useCache: false ) ) @@ -7818,6 +7821,7 @@ final class WorkspaceTests: XCTestCase { authorizationProvider: .none, hostToolchain: UserToolchain(swiftSDK: .hostSwiftSDK()), checksumAlgorithm: checksumAlgorithm, + cachePath: .none, customHTTPClient: .none, customArchiver: .none, delegate: .none @@ -8027,7 +8031,6 @@ final class WorkspaceTests: XCTestCase { func testArtifactDownloadAddsAcceptHeader() throws { let sandbox = AbsolutePath("/tmp/ws/") let fs = InMemoryFileSystem() - let downloads = ThreadSafeKeyValueStore() var acceptHeaders: [String] = [] // returns a dummy zipfile for the requested artifact @@ -8052,7 +8055,6 @@ final class WorkspaceTests: XCTestCase { atomically: true ) - downloads[request.url] = destination completion(.success(.okay())) } catch { completion(.failure(error)) @@ -8106,6 +8108,207 @@ final class WorkspaceTests: XCTestCase { } } + func testDownloadedArtifactNoCache() throws { + let sandbox = AbsolutePath("/tmp/ws/") + let fs = InMemoryFileSystem() + var downloads = 0 + + // returns a dummy zipfile for the requested artifact + let httpClient = LegacyHTTPClient(handler: { request, _, completion in + do { + guard case .download(let fileSystem, let destination) = request.kind else { + throw StringError("invalid request \(request.kind)") + } + + let contents: [UInt8] + switch request.url.lastPathComponent { + case "a1.zip": + contents = [0xA1] + default: + throw StringError("unexpected url \(request.url)") + } + + try fileSystem.writeFileContents( + destination, + bytes: ByteString(contents), + atomically: true + ) + + downloads += 1 + completion(.success(.okay())) + } catch { + completion(.failure(error)) + } + }) + + // create a dummy xcframework directory from the request archive + let archiver = MockArchiver(handler: { archiver, archivePath, destinationPath, completion in + do { + switch archivePath.basename { + case "a1.zip": + try createDummyXCFramework(fileSystem: fs, path: destinationPath, name: "A1") + default: + throw StringError("unexpected archivePath \(archivePath)") + } + archiver.extractions + .append(MockArchiver.Extraction(archivePath: archivePath, destinationPath: destinationPath)) + completion(.success(())) + } catch { + completion(.failure(error)) + } + }) + + let workspace = try MockWorkspace( + sandbox: sandbox, + fileSystem: fs, + roots: [ + MockPackage( + name: "Root", + targets: [ + MockTarget( + name: "A1", + type: .binary, + url: "https://a.com/a1.zip", + checksum: "a1" + ), + ] + ), + ], + binaryArtifactsManager: .init( + httpClient: httpClient, + archiver: archiver, + useCache: false + ) + ) + + // should not come from cache + try workspace.checkPackageGraph(roots: ["Root"]) { _, diagnostics in + XCTAssertNoDiagnostics(diagnostics) + XCTAssertEqual(downloads, 1) + } + + // state is there, should not come from local cache + try workspace.checkPackageGraph(roots: ["Root"]) { _, diagnostics in + XCTAssertNoDiagnostics(diagnostics) + XCTAssertEqual(downloads, 1) + } + + // reseting state, should not come from global cache + try workspace.resetState() + try workspace.checkPackageGraph(roots: ["Root"]) { _, diagnostics in + XCTAssertNoDiagnostics(diagnostics) + XCTAssertEqual(downloads, 2) + } + } + + func testDownloadedArtifactCache() throws { + let sandbox = AbsolutePath("/tmp/ws/") + let fs = InMemoryFileSystem() + var downloads = 0 + + // returns a dummy zipfile for the requested artifact + let httpClient = LegacyHTTPClient(handler: { request, _, completion in + do { + guard case .download(let fileSystem, let destination) = request.kind else { + throw StringError("invalid request \(request.kind)") + } + + let contents: [UInt8] + switch request.url.lastPathComponent { + case "a1.zip": + contents = [0xA1] + default: + throw StringError("unexpected url \(request.url)") + } + + try fileSystem.writeFileContents( + destination, + bytes: ByteString(contents), + atomically: true + ) + + downloads += 1 + completion(.success(.okay())) + } catch { + completion(.failure(error)) + } + }) + + // create a dummy xcframework directory from the request archive + let archiver = MockArchiver(handler: { archiver, archivePath, destinationPath, completion in + do { + switch archivePath.basename { + case "a1.zip": + try createDummyXCFramework(fileSystem: fs, path: destinationPath, name: "A1") + default: + throw StringError("unexpected archivePath \(archivePath)") + } + archiver.extractions + .append(MockArchiver.Extraction(archivePath: archivePath, destinationPath: destinationPath)) + completion(.success(())) + } catch { + completion(.failure(error)) + } + }) + + let workspace = try MockWorkspace( + sandbox: sandbox, + fileSystem: fs, + roots: [ + MockPackage( + name: "Root", + targets: [ + MockTarget( + name: "A1", + type: .binary, + url: "https://a.com/a1.zip", + checksum: "a1" + ), + ] + ), + ], + binaryArtifactsManager: .init( + httpClient: httpClient, + archiver: archiver, + useCache: true + ) + ) + + // should not come from cache + try workspace.checkPackageGraph(roots: ["Root"]) { _, diagnostics in + XCTAssertNoDiagnostics(diagnostics) + XCTAssertEqual(downloads, 1) + } + + // state is there, should not come from local cache + try workspace.checkPackageGraph(roots: ["Root"]) { _, diagnostics in + XCTAssertNoDiagnostics(diagnostics) + XCTAssertEqual(downloads, 1) + } + + // reseting state, should come from global cache + try workspace.resetState() + try workspace.checkPackageGraph(roots: ["Root"]) { _, diagnostics in + XCTAssertNoDiagnostics(diagnostics) + XCTAssertEqual(downloads, 1) + } + + // delete global cache, should download again + try workspace.resetState() + try fs.removeFileTree(fs.swiftPMCacheDirectory) + try workspace.checkPackageGraph(roots: ["Root"]) { _, diagnostics in + XCTAssertNoDiagnostics(diagnostics) + XCTAssertEqual(downloads, 2) + } + + // reseting state, should come from global cache again + try workspace.resetState() + try workspace.checkPackageGraph(roots: ["Root"]) { _, diagnostics in + XCTAssertNoDiagnostics(diagnostics) + XCTAssertEqual(downloads, 2) + } + } + func testDownloadedArtifactTransitive() throws { let sandbox = AbsolutePath("/tmp/ws/") let fs = InMemoryFileSystem() @@ -8244,7 +8447,8 @@ final class WorkspaceTests: XCTestCase { ], binaryArtifactsManager: .init( httpClient: httpClient, - archiver: archiver + archiver: archiver, + useCache: false ) ) @@ -8363,7 +8567,8 @@ final class WorkspaceTests: XCTestCase { ], binaryArtifactsManager: .init( httpClient: httpClient, - archiver: archiver + archiver: archiver, + useCache: false ) ) @@ -8618,7 +8823,8 @@ final class WorkspaceTests: XCTestCase { ], binaryArtifactsManager: .init( httpClient: httpClient, - archiver: archiver + archiver: archiver, + useCache: false ) ) @@ -8758,7 +8964,8 @@ final class WorkspaceTests: XCTestCase { ], binaryArtifactsManager: .init( httpClient: httpClient, - archiver: archiver + archiver: archiver, + useCache: false ) ) @@ -9017,7 +9224,8 @@ final class WorkspaceTests: XCTestCase { ], binaryArtifactsManager: .init( httpClient: httpClient, - archiver: archiver + archiver: archiver, + useCache: false ), checksumAlgorithm: checksumAlgorithm ) From 8b7f4a2326672d1b52416afa9bdc1a95a5875156 Mon Sep 17 00:00:00 2001 From: tomer doron Date: Thu, 16 Nov 2023 13:51:34 -0800 Subject: [PATCH 2/4] Update Sources/Commands/ToolWorkspaceDelegate.swift Co-authored-by: Max Desiatov --- Sources/Commands/ToolWorkspaceDelegate.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/Commands/ToolWorkspaceDelegate.swift b/Sources/Commands/ToolWorkspaceDelegate.swift index 1c263106073..d9fd9e124ad 100644 --- a/Sources/Commands/ToolWorkspaceDelegate.swift +++ b/Sources/Commands/ToolWorkspaceDelegate.swift @@ -160,7 +160,7 @@ class ToolWorkspaceDelegate: WorkspaceDelegate { } if fetchDetails.fromCache { - self.outputHandler("Fetched \(url) (\(duration.descriptionInSeconds))", false) + self.outputHandler("Fetched \(url) from cache (\(duration.descriptionInSeconds))", false) } else { self.outputHandler("Downloaded \(url) (\(duration.descriptionInSeconds))", false) } From da48f7e8f4193f14371eaadc16d79903a16f732b Mon Sep 17 00:00:00 2001 From: tomer doron Date: Thu, 16 Nov 2023 13:51:40 -0800 Subject: [PATCH 3/4] Update Sources/Workspace/Workspace+BinaryArtifacts.swift Co-authored-by: Max Desiatov --- Sources/Workspace/Workspace+BinaryArtifacts.swift | 2 -- 1 file changed, 2 deletions(-) diff --git a/Sources/Workspace/Workspace+BinaryArtifacts.swift b/Sources/Workspace/Workspace+BinaryArtifacts.swift index 7fee94f3230..d0f464b4c6b 100644 --- a/Sources/Workspace/Workspace+BinaryArtifacts.swift +++ b/Sources/Workspace/Workspace+BinaryArtifacts.swift @@ -675,8 +675,6 @@ public protocol BinaryArtifactsManagerDelegate { func downloadingBinaryArtifact(from url: String, bytesDownloaded: Int64, totalBytesToDownload: Int64?) /// The workspace finished downloading all binary artifacts. func didDownloadAllBinaryArtifacts() - - } extension Workspace.BinaryArtifactsManager { From 5a67ad3f1f2efcde4d1d507fceccac8c304a806b Mon Sep 17 00:00:00 2001 From: tomer doron Date: Thu, 16 Nov 2023 14:07:46 -0800 Subject: [PATCH 4/4] fixuo --- Sources/Commands/ToolWorkspaceDelegate.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/Commands/ToolWorkspaceDelegate.swift b/Sources/Commands/ToolWorkspaceDelegate.swift index d9fd9e124ad..983b0a75a68 100644 --- a/Sources/Commands/ToolWorkspaceDelegate.swift +++ b/Sources/Commands/ToolWorkspaceDelegate.swift @@ -81,7 +81,7 @@ class ToolWorkspaceDelegate: WorkspaceDelegate { } } - self.outputHandler("Fetched \(packageLocation ?? package.description) (\(duration.descriptionInSeconds))", false) + self.outputHandler("Fetched \(packageLocation ?? package.description) from cache (\(duration.descriptionInSeconds))", false) } func fetchingPackage(package: PackageIdentity, packageLocation: String?, progress: Int64, total: Int64?) {