@@ -22,6 +22,8 @@ public final class SwiftSDKBundleStore {
2222 public enum Output : Equatable , CustomStringConvertible {
2323 case downloadStarted( URL )
2424 case downloadFinishedSuccessfully( URL )
25+ case verifyingChecksum
26+ case checksumValid
2527 case unpackingArchive( bundlePathOrURL: String )
2628 case installationSuccessful( bundlePathOrURL: String , bundleName: String )
2729
@@ -31,6 +33,10 @@ public final class SwiftSDKBundleStore {
3133 return " Downloading a Swift SDK bundle archive from ` \( url) `... "
3234 case let . downloadFinishedSuccessfully( url) :
3335 return " Swift SDK bundle archive successfully downloaded from ` \( url) `. "
36+ case . verifyingChecksum:
37+ return " Verifying if checksum of the downloaded archive is valid... "
38+ case . checksumValid:
39+ return " Downloaded archive has a valid checksum. "
3440 case let . installationSuccessful( bundlePathOrURL, bundleName) :
3541 return " Swift SDK bundle at ` \( bundlePathOrURL) ` successfully installed as \( bundleName) . "
3642 case let . unpackingArchive( bundlePathOrURL) :
@@ -145,8 +151,10 @@ public final class SwiftSDKBundleStore {
145151 /// - archiver: Archiver instance to use for extracting bundle archives.
146152 public func install(
147153 bundlePathOrURL: String ,
154+ checksum: String ? = nil ,
148155 _ archiver: any Archiver ,
149- _ httpClient: HTTPClient = . init( )
156+ _ httpClient: HTTPClient = . init( ) ,
157+ hasher: ( ( _ archivePath: AbsolutePath ) throws -> String ) ? = nil
150158 ) async throws {
151159 let bundleName = try await withTemporaryDirectory ( fileSystem: self . fileSystem, removeTreeOnDeinit: true ) { temporaryDirectory in
152160 let bundlePath : AbsolutePath
@@ -156,9 +164,13 @@ public final class SwiftSDKBundleStore {
156164 let scheme = bundleURL. scheme,
157165 scheme == " http " || scheme == " https "
158166 {
167+ guard let checksum, let hasher else {
168+ throw SwiftSDKError . checksumNotProvided ( bundleURL)
169+ }
170+
159171 let bundleName : String
160172 let fileNameComponent = bundleURL. lastPathComponent
161- if archiver. supportedExtensions . contains ( where : { fileNameComponent. hasSuffix ( $0 ) } ) {
173+ if archiver. isFileSupported ( fileNameComponent) {
162174 bundleName = fileNameComponent
163175 } else {
164176 // Assume that the bundle is a tarball if it doesn't have a recognized extension.
@@ -193,9 +205,16 @@ public final class SwiftSDKBundleStore {
193205 )
194206 self . downloadProgressAnimation? . complete ( success: true )
195207
196- bundlePath = downloadedBundlePath
197-
198208 self . outputHandler ( . downloadFinishedSuccessfully( bundleURL) )
209+
210+ self . outputHandler ( . verifyingChecksum)
211+ let computedChecksum = try hasher ( downloadedBundlePath)
212+ guard computedChecksum == checksum else {
213+ throw SwiftSDKError . checksumInvalid ( computed: computedChecksum, provided: checksum)
214+ }
215+ self . outputHandler ( . checksumValid)
216+
217+ bundlePath = downloadedBundlePath
199218 } else if
200219 let cwd: AbsolutePath = self . fileSystem. currentWorkingDirectory,
201220 let originalBundlePath = try ? AbsolutePath ( validating: bundlePathOrURL, relativeTo: cwd)
0 commit comments