@@ -22,6 +22,8 @@ public final class SwiftSDKBundleStore {
22
22
public enum Output : Equatable , CustomStringConvertible {
23
23
case downloadStarted( URL )
24
24
case downloadFinishedSuccessfully( URL )
25
+ case verifyingChecksum
26
+ case checksumValid
25
27
case unpackingArchive( bundlePathOrURL: String )
26
28
case installationSuccessful( bundlePathOrURL: String , bundleName: String )
27
29
@@ -31,6 +33,10 @@ public final class SwiftSDKBundleStore {
31
33
return " Downloading a Swift SDK bundle archive from ` \( url) `... "
32
34
case let . downloadFinishedSuccessfully( url) :
33
35
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. "
34
40
case let . installationSuccessful( bundlePathOrURL, bundleName) :
35
41
return " Swift SDK bundle at ` \( bundlePathOrURL) ` successfully installed as \( bundleName) . "
36
42
case let . unpackingArchive( bundlePathOrURL) :
@@ -145,8 +151,10 @@ public final class SwiftSDKBundleStore {
145
151
/// - archiver: Archiver instance to use for extracting bundle archives.
146
152
public func install(
147
153
bundlePathOrURL: String ,
154
+ checksum: String ? = nil ,
148
155
_ archiver: any Archiver ,
149
- _ httpClient: HTTPClient = . init( )
156
+ _ httpClient: HTTPClient = . init( ) ,
157
+ hasher: ( ( _ archivePath: AbsolutePath ) throws -> String ) ? = nil
150
158
) async throws {
151
159
let bundleName = try await withTemporaryDirectory ( fileSystem: self . fileSystem, removeTreeOnDeinit: true ) { temporaryDirectory in
152
160
let bundlePath : AbsolutePath
@@ -156,9 +164,13 @@ public final class SwiftSDKBundleStore {
156
164
let scheme = bundleURL. scheme,
157
165
scheme == " http " || scheme == " https "
158
166
{
167
+ guard let checksum, let hasher else {
168
+ throw SwiftSDKError . checksumNotProvided ( bundleURL)
169
+ }
170
+
159
171
let bundleName : String
160
172
let fileNameComponent = bundleURL. lastPathComponent
161
- if archiver. supportedExtensions . contains ( where : { fileNameComponent. hasSuffix ( $0 ) } ) {
173
+ if archiver. isFileSupported ( fileNameComponent) {
162
174
bundleName = fileNameComponent
163
175
} else {
164
176
// Assume that the bundle is a tarball if it doesn't have a recognized extension.
@@ -193,9 +205,16 @@ public final class SwiftSDKBundleStore {
193
205
)
194
206
self . downloadProgressAnimation? . complete ( success: true )
195
207
196
- bundlePath = downloadedBundlePath
197
-
198
208
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
199
218
} else if
200
219
let cwd: AbsolutePath = self . fileSystem. currentWorkingDirectory,
201
220
let originalBundlePath = try ? AbsolutePath ( validating: bundlePathOrURL, relativeTo: cwd)
0 commit comments