Skip to content

Commit 95ce2a3

Browse files
authored
Make sure the workspace state is saved after adding/removing artifacts. (#8475)
1 parent 9fb4efb commit 95ce2a3

File tree

2 files changed

+48
-18
lines changed

2 files changed

+48
-18
lines changed

Sources/Basics/CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ add_library(Basics
2424
Concurrency/ThreadSafeArrayStore.swift
2525
Concurrency/ThreadSafeBox.swift
2626
Concurrency/ThreadSafeKeyValueStore.swift
27+
Concurrency/ThrowingDefer.swift
2728
Concurrency/TokenBucket.swift
2829
DispatchTimeInterval+Extensions.swift
2930
Environment/Environment.swift

Sources/Workspace/Workspace+BinaryArtifacts.swift

+47-18
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,7 @@ extension Workspace {
155155
if !indexFiles.isEmpty {
156156
let errors = ThreadSafeArrayStore<Error>()
157157

158-
zipArtifacts.append(contentsOf: try await withThrowingTaskGroup(
158+
try await zipArtifacts.append(contentsOf: withThrowingTaskGroup(
159159
of: RemoteArtifact?.self,
160160
returning: [RemoteArtifact].self
161161
) { group in
@@ -164,7 +164,8 @@ extension Workspace {
164164
group.addTask {
165165
var request = HTTPClient.Request(method: .get, url: indexFile.url)
166166
request.options.validResponseCodes = [200]
167-
request.options.authorizationProvider = self.authorizationProvider?.httpAuthorizationHeader(for:)
167+
request.options.authorizationProvider = self.authorizationProvider?
168+
.httpAuthorizationHeader(for:)
168169
do {
169170
let response = try await self.httpClient.execute(request)
170171
guard let body = response.body else {
@@ -227,7 +228,10 @@ extension Workspace {
227228
group.addTask { () -> ManagedArtifact? in
228229
let destinationDirectory = artifactsDirectory
229230
.appending(components: [artifact.packageRef.identity.description, artifact.targetName])
230-
guard observabilityScope.trap({ try fileSystem.createDirectory(destinationDirectory, recursive: true) })
231+
guard observabilityScope.trap({ try fileSystem.createDirectory(
232+
destinationDirectory,
233+
recursive: true
234+
) })
231235
else {
232236
return nil
233237
}
@@ -295,7 +299,8 @@ extension Workspace {
295299
return nil
296300
}
297301

298-
observabilityScope.emit(debug: "extracting \(archivePath) to \(tempExtractionDirectory)")
302+
observabilityScope
303+
.emit(debug: "extracting \(archivePath) to \(tempExtractionDirectory)")
299304
do {
300305
try await self.archiver.extract(
301306
from: archivePath,
@@ -326,7 +331,8 @@ extension Workspace {
326331
debug: "no first level component stripping needed for \(tempExtractionDirectory)"
327332
)
328333
}
329-
let content = try self.fileSystem.getDirectoryContents(tempExtractionDirectory)
334+
let content = try self.fileSystem
335+
.getDirectoryContents(tempExtractionDirectory)
330336
// copy from temp location to actual location
331337
for file in content {
332338
let source = tempExtractionDirectory
@@ -350,8 +356,8 @@ extension Workspace {
350356
observabilityScope: observabilityScope
351357
) else {
352358
observabilityScope.emit(BinaryArtifactsManagerError.remoteArtifactNotFound(
353-
artifactURL: artifact.url,
354-
targetName: artifact.targetName
359+
artifactURL: artifact.url,
360+
targetName: artifact.targetName
355361
))
356362
return nil
357363
}
@@ -432,7 +438,7 @@ extension Workspace {
432438
artifactsDirectory: AbsolutePath,
433439
observabilityScope: ObservabilityScope
434440
) async throws -> [ManagedArtifact] {
435-
return try await withThrowingTaskGroup(of: ManagedArtifact?.self) { group in
441+
try await withThrowingTaskGroup(of: ManagedArtifact?.self) { group in
436442
for artifact in artifacts {
437443
group.addTask { () -> ManagedArtifact? in
438444
let destinationDirectory = artifactsDirectory
@@ -458,7 +464,9 @@ extension Workspace {
458464
acceptableExtensions: BinaryModule.Kind.allCases.map(\.fileExtension)
459465
) {
460466
observabilityScope
461-
.emit(debug: "stripping first level component from \(tempExtractionDirectory)")
467+
.emit(
468+
debug: "stripping first level component from \(tempExtractionDirectory)"
469+
)
462470
try self.fileSystem.stripFirstLevel(of: tempExtractionDirectory)
463471
} else {
464472
observabilityScope.emit(
@@ -564,7 +572,7 @@ extension Workspace {
564572
artifact: RemoteArtifact,
565573
destination: AbsolutePath,
566574
observabilityScope: ObservabilityScope,
567-
progress: @escaping @Sendable (Int64, Optional<Int64>) -> Void
575+
progress: @escaping @Sendable (Int64, Int64?) -> Void
568576
) async throws -> Bool {
569577
// not using cache, download directly
570578
guard let cachePath = self.cachePath else {
@@ -591,7 +599,8 @@ extension Workspace {
591599
let cachedArtifactPath = cachePath.appending(cacheKey)
592600

593601
if self.fileSystem.exists(cachedArtifactPath) {
594-
observabilityScope.emit(debug: "copying cached binary artifact for \(artifact.url) from \(cachedArtifactPath)")
602+
observabilityScope
603+
.emit(debug: "copying cached binary artifact for \(artifact.url) from \(cachedArtifactPath)")
595604
self.delegate?.willDownloadBinaryArtifact(from: artifact.url.absoluteString, fromCache: true)
596605

597606
// copy from cache to destination
@@ -600,7 +609,8 @@ extension Workspace {
600609
}
601610

602611
// download to the cache
603-
observabilityScope.emit(debug: "downloading binary artifact for \(artifact.url) to cache at \(cachedArtifactPath)")
612+
observabilityScope
613+
.emit(debug: "downloading binary artifact for \(artifact.url) to cache at \(cachedArtifactPath)")
604614

605615
self.delegate?.willDownloadBinaryArtifact(from: artifact.url.absoluteString, fromCache: false)
606616

@@ -623,7 +633,7 @@ extension Workspace {
623633
artifact: RemoteArtifact,
624634
destination: AbsolutePath,
625635
observabilityScope: ObservabilityScope,
626-
progress: @escaping @Sendable (Int64, Optional<Int64>) -> Void
636+
progress: @escaping @Sendable (Int64, Int64?) -> Void
627637
) async throws {
628638
observabilityScope.emit(debug: "downloading \(artifact.url) to \(destination)")
629639

@@ -800,6 +810,26 @@ extension Workspace {
800810
manifests: DependencyManifests,
801811
addedOrUpdatedPackages: [PackageReference],
802812
observabilityScope: ObservabilityScope
813+
) async throws {
814+
try await withAsyncThrowing {
815+
try await self._updateBinaryArtifacts(
816+
manifests: manifests,
817+
addedOrUpdatedPackages: addedOrUpdatedPackages,
818+
observabilityScope: observabilityScope
819+
)
820+
} defer: {
821+
// Make sure the workspace state is saved exactly once, even if the method exits early.
822+
// Files may have been deleted, download, etc. and the state needs to reflect that.
823+
await observabilityScope.trap {
824+
try await self.state.save()
825+
}
826+
}
827+
}
828+
829+
private func _updateBinaryArtifacts(
830+
manifests: DependencyManifests,
831+
addedOrUpdatedPackages: [PackageReference],
832+
observabilityScope: ObservabilityScope
803833
) async throws {
804834
let manifestArtifacts = try self.binaryArtifactsManager.parseArtifacts(
805835
from: manifests,
@@ -893,7 +923,10 @@ extension Workspace {
893923
// Remove the artifacts and directories which are not needed anymore.
894924
await observabilityScope.trap {
895925
for artifact in artifactsToRemove {
896-
await state.artifacts.remove(packageIdentity: artifact.packageRef.identity, targetName: artifact.targetName)
926+
await state.artifacts.remove(
927+
packageIdentity: artifact.packageRef.identity,
928+
targetName: artifact.targetName
929+
)
897930

898931
if isAtArtifactsDirectory(artifact) {
899932
try fileSystem.removeFileTree(artifact.path)
@@ -937,10 +970,6 @@ extension Workspace {
937970
throw Diagnostics.fatalError
938971
}
939972

940-
await observabilityScope.trap {
941-
try await self.state.save()
942-
}
943-
944973
func isAtArtifactsDirectory(_ artifact: ManagedArtifact) -> Bool {
945974
artifact.path.isDescendant(of: self.location.artifactsDirectory)
946975
}

0 commit comments

Comments
 (0)