@@ -2539,85 +2539,105 @@ extension Workspace {
2539
2539
semaphore. signal ( )
2540
2540
}
2541
2541
2542
+ // TODO: Use the same extraction logic for both remote and local archived artifacts.
2542
2543
switch downloadResult {
2543
2544
case . success:
2544
- guard let archiveChecksum = observabilityScope. trap ( { try self . checksum ( forBinaryArtifactAt: archivePath) } ) else {
2545
- return
2546
- }
2547
- guard archiveChecksum == artifact. checksum else {
2548
- observabilityScope. emit ( . artifactInvalidChecksum( targetName: artifact. targetName, expectedChecksum: artifact. checksum, actualChecksum: archiveChecksum) )
2549
- observabilityScope. trap { try self . fileSystem. removeFileTree ( archivePath) }
2550
- return
2551
- }
2552
2545
2553
- guard let tempExtractionDirectory = observabilityScope. trap ( { ( ) -> AbsolutePath in
2554
- let path = self . location. artifactsDirectory. appending ( components: " extract " , artifact. packageRef. identity. description, artifact. targetName, UUID ( ) . uuidString)
2555
- try self . fileSystem. forceCreateDirectory ( at: path)
2556
- return path
2557
- } ) else {
2558
- return
2559
- }
2560
-
2561
- // TODO: Use the same extraction logic for both remote and local archived artifacts.
2562
2546
group. enter ( )
2563
- observabilityScope. emit ( debug: " extracting \( archivePath) to \( tempExtractionDirectory ) " )
2564
- self . archiver. extract ( from : archivePath, to : tempExtractionDirectory , completion: { extractResult in
2547
+ observabilityScope. emit ( debug: " validating \( archivePath) " )
2548
+ self . archiver. validate ( path : archivePath, completion: { validationResult in
2565
2549
defer { group. leave ( ) }
2566
2550
2567
- switch extractResult {
2568
- case . success:
2569
- var artifactPath : AbsolutePath ? = nil
2570
- observabilityScope. trap {
2571
- try self . fileSystem. withLock ( on: parentDirectory, type: . exclusive) {
2572
- // strip first level component if needed
2573
- if try self . fileSystem. shouldStripFirstLevel ( archiveDirectory: tempExtractionDirectory, acceptableExtensions: BinaryTarget . Kind. allCases. map ( { $0. fileExtension } ) ) {
2574
- observabilityScope. emit ( debug: " stripping first level component from \( tempExtractionDirectory) " )
2575
- try self . fileSystem. stripFirstLevel ( of: tempExtractionDirectory)
2576
- } else {
2577
- observabilityScope. emit ( debug: " no first level component stripping needed for \( tempExtractionDirectory) " )
2578
- }
2579
- let content = try self . fileSystem. getDirectoryContents ( tempExtractionDirectory)
2580
- // copy from temp location to actual location
2581
- for file in content {
2582
- let source = tempExtractionDirectory. appending ( component: file)
2583
- let destination = parentDirectory. appending ( component: file)
2584
- if self . fileSystem. exists ( destination) {
2585
- try self . fileSystem. removeFileTree ( destination)
2586
- }
2587
- try self . fileSystem. copy ( from: source, to: destination)
2588
- if destination. basenameWithoutExt == artifact. targetName {
2589
- artifactPath = destination
2590
- }
2591
- }
2592
- }
2593
- // remove temp location
2594
- try self . fileSystem. removeFileTree ( tempExtractionDirectory)
2551
+ switch validationResult {
2552
+ case . success( let valid) :
2553
+ guard valid else {
2554
+ observabilityScope. emit ( . artifactInvalidArchive( artifactURL: artifact. url, targetName: artifact. targetName) )
2555
+ return
2556
+ }
2557
+
2558
+ guard let archiveChecksum = observabilityScope. trap ( { try self . checksum ( forBinaryArtifactAt: archivePath) } ) else {
2559
+ return
2560
+ }
2561
+ guard archiveChecksum == artifact. checksum else {
2562
+ observabilityScope. emit ( . artifactInvalidChecksum( targetName: artifact. targetName, expectedChecksum: artifact. checksum, actualChecksum: archiveChecksum) )
2563
+ observabilityScope. trap { try self . fileSystem. removeFileTree ( archivePath) }
2564
+ return
2595
2565
}
2596
2566
2597
- guard let mainArtifactPath = artifactPath else {
2598
- return observabilityScope. emit ( . artifactNotFound( targetName: artifact. targetName, artifactName: artifact. targetName) )
2567
+ guard let tempExtractionDirectory = observabilityScope. trap ( { ( ) -> AbsolutePath in
2568
+ let path = self . location. artifactsDirectory. appending ( components: " extract " , artifact. packageRef. identity. description, artifact. targetName, UUID ( ) . uuidString)
2569
+ try self . fileSystem. forceCreateDirectory ( at: path)
2570
+ return path
2571
+ } ) else {
2572
+ return
2599
2573
}
2600
2574
2601
- result. append (
2602
- . remote(
2603
- packageRef: artifact. packageRef,
2604
- targetName: artifact. targetName,
2605
- url: artifact. url. absoluteString,
2606
- checksum: artifact. checksum,
2607
- path: mainArtifactPath
2608
- )
2609
- )
2610
- self . delegate? . didDownloadBinaryArtifact ( from: artifact. url. absoluteString, result: . success( mainArtifactPath) , duration: downloadStart. distance ( to: . now( ) ) )
2575
+ group. enter ( )
2576
+ observabilityScope. emit ( debug: " extracting \( archivePath) to \( tempExtractionDirectory) " )
2577
+ self . archiver. extract ( from: archivePath, to: tempExtractionDirectory, completion: { extractResult in
2578
+ defer { group. leave ( ) }
2579
+
2580
+ switch extractResult {
2581
+ case . success:
2582
+ var artifactPath : AbsolutePath ? = nil
2583
+ observabilityScope. trap {
2584
+ try self . fileSystem. withLock ( on: parentDirectory, type: . exclusive) {
2585
+ // strip first level component if needed
2586
+ if try self . fileSystem. shouldStripFirstLevel ( archiveDirectory: tempExtractionDirectory, acceptableExtensions: BinaryTarget . Kind. allCases. map ( { $0. fileExtension } ) ) {
2587
+ observabilityScope. emit ( debug: " stripping first level component from \( tempExtractionDirectory) " )
2588
+ try self . fileSystem. stripFirstLevel ( of: tempExtractionDirectory)
2589
+ } else {
2590
+ observabilityScope. emit ( debug: " no first level component stripping needed for \( tempExtractionDirectory) " )
2591
+ }
2592
+ let content = try self . fileSystem. getDirectoryContents ( tempExtractionDirectory)
2593
+ // copy from temp location to actual location
2594
+ for file in content {
2595
+ let source = tempExtractionDirectory. appending ( component: file)
2596
+ let destination = parentDirectory. appending ( component: file)
2597
+ if self . fileSystem. exists ( destination) {
2598
+ try self . fileSystem. removeFileTree ( destination)
2599
+ }
2600
+ try self . fileSystem. copy ( from: source, to: destination)
2601
+ if destination. basenameWithoutExt == artifact. targetName {
2602
+ artifactPath = destination
2603
+ }
2604
+ }
2605
+ }
2606
+ // remove temp location
2607
+ try self . fileSystem. removeFileTree ( tempExtractionDirectory)
2608
+ }
2609
+
2610
+ guard let mainArtifactPath = artifactPath else {
2611
+ return observabilityScope. emit ( . artifactNotFound( targetName: artifact. targetName, artifactName: artifact. targetName) )
2612
+ }
2613
+
2614
+ result. append (
2615
+ . remote(
2616
+ packageRef: artifact. packageRef,
2617
+ targetName: artifact. targetName,
2618
+ url: artifact. url. absoluteString,
2619
+ checksum: artifact. checksum,
2620
+ path: mainArtifactPath
2621
+ )
2622
+ )
2623
+ self . delegate? . didDownloadBinaryArtifact ( from: artifact. url. absoluteString, result: . success( mainArtifactPath) , duration: downloadStart. distance ( to: . now( ) ) )
2624
+ case . failure( let error) :
2625
+ let reason = ( error as? LocalizedError ) ? . errorDescription ?? " \( error) "
2626
+ observabilityScope. emit ( . artifactFailedExtraction( artifactURL: artifact. url, targetName: artifact. targetName, reason: reason) )
2627
+ self . delegate? . didDownloadBinaryArtifact ( from: artifact. url. absoluteString, result: . failure( error) , duration: downloadStart. distance ( to: . now( ) ) )
2628
+ }
2629
+
2630
+ observabilityScope. trap { try self . fileSystem. removeFileTree ( archivePath) }
2631
+ } )
2611
2632
case . failure( let error) :
2612
- let reason = ( error as? LocalizedError ) ? . errorDescription ?? error. localizedDescription
2613
- observabilityScope. emit ( . artifactFailedExtraction ( artifactURL: artifact. url, targetName: artifact. targetName, reason: reason) )
2633
+ let reason = ( error as? LocalizedError ) ? . errorDescription ?? " \( error) "
2634
+ observabilityScope. emit ( . artifactFailedValidation ( artifactURL: artifact. url, targetName: artifact. targetName, reason: " \( reason) " ) )
2614
2635
self . delegate? . didDownloadBinaryArtifact ( from: artifact. url. absoluteString, result: . failure( error) , duration: downloadStart. distance ( to: . now( ) ) )
2615
2636
}
2616
-
2617
- observabilityScope. trap { try self . fileSystem. removeFileTree ( archivePath) }
2618
2637
} )
2619
2638
case . failure( let error) :
2620
- observabilityScope. emit ( . artifactFailedDownload( artifactURL: artifact. url, targetName: artifact. targetName, reason: " \( error) " ) )
2639
+ let reason = ( error as? LocalizedError ) ? . errorDescription ?? " \( error) "
2640
+ observabilityScope. emit ( . artifactFailedDownload( artifactURL: artifact. url, targetName: artifact. targetName, reason: " \( reason) " ) )
2621
2641
self . delegate? . didDownloadBinaryArtifact ( from: artifact. url. absoluteString, result: . failure( error) , duration: downloadStart. distance ( to: . now( ) ) )
2622
2642
}
2623
2643
} )
0 commit comments