@@ -42,7 +42,8 @@ public struct Linux: Platform {
4242 " tar.gz "
4343 }
4444
45- private static let skipVerificationMessage : String = " To skip signature verification, specify the --no-verify flag. "
45+ private static let skipVerificationMessage : String =
46+ " To skip signature verification, specify the --no-verify flag. "
4647
4748 public func verifySwiftlySystemPrerequisites( ) throws {
4849 // Check if the root CA certificates are installed on this system for NIOSSL to use.
@@ -67,10 +68,15 @@ public struct Linux: Platform {
6768 }
6869 }
6970
70- public func verifySystemPrerequisitesForInstall( _ ctx: SwiftlyCoreContext , platformName: String , version _: ToolchainVersion , requireSignatureValidation: Bool ) async throws -> String ? {
71+ public func verifySystemPrerequisitesForInstall(
72+ _ ctx: SwiftlyCoreContext , platformName: String , version _: ToolchainVersion ,
73+ requireSignatureValidation: Bool
74+ ) async throws -> String ? {
7175 // TODO: these are hard-coded until we have a place to query for these based on the toolchain version
7276 // These lists were copied from the dockerfile sources here: https://github.com/apple/swift-docker/tree/ea035798755cce4ec41e0c6dbdd320904cef0421/5.10
73- let packages : [ String ] = switch platformName {
77+ let packages : [ String ] =
78+ switch platformName
79+ {
7480 case " ubuntu1804 " :
7581 [
7682 " libatomic1 " ,
@@ -221,7 +227,9 @@ public struct Linux: Platform {
221227 [ ]
222228 }
223229
224- let manager : String ? = switch platformName {
230+ let manager : String ? =
231+ switch platformName
232+ {
225233 case " ubuntu1804 " :
226234 " apt-get "
227235 case " ubuntu2004 " :
@@ -259,18 +267,19 @@ public struct Linux: Platform {
259267 }
260268
261269 let tmpFile = self . getTempFilePath ( )
262- let _ = FileManager . default. createFile ( atPath: tmpFile. path, contents: nil , attributes: [ . posixPermissions: 0o600 ] )
270+ let _ = FileManager . default. createFile (
271+ atPath: tmpFile. path, contents: nil , attributes: [ . posixPermissions: 0o600 ]
272+ )
263273 defer {
264274 try ? FileManager . default. removeItem ( at: tmpFile)
265275 }
266276
267- guard let url = URL ( string: " https://www.swift.org/keys/all-keys.asc " ) else {
268- throw SwiftlyError ( message: " malformed URL to the swift gpg keys " )
269- }
270-
271- try await ctx. httpClient. downloadFile ( url: url, to: tmpFile)
277+ try await ctx. httpClient. getGpgKeys ( ) . download ( to: tmpFile)
272278 if let mockedHomeDir = ctx. mockedHomeDir {
273- try self . runProgram ( " gpg " , " --import " , tmpFile. path, quiet: true , env: [ " GNUPGHOME " : mockedHomeDir. appendingPathComponent ( " .gnupg " ) . path] )
279+ try self . runProgram (
280+ " gpg " , " --import " , tmpFile. path, quiet: true ,
281+ env: [ " GNUPGHOME " : mockedHomeDir. appendingPathComponent ( " .gnupg " ) . path]
282+ )
274283 } else {
275284 try self . runProgram ( " gpg " , " --import " , tmpFile. path, quiet: true )
276285 }
@@ -323,16 +332,20 @@ public struct Linux: Platform {
323332 }
324333 }
325334
326- public func install( _ ctx: SwiftlyCoreContext , from tmpFile: URL , version: ToolchainVersion , verbose: Bool ) throws {
335+ public func install(
336+ _ ctx: SwiftlyCoreContext , from tmpFile: URL , version: ToolchainVersion , verbose: Bool
337+ ) async throws {
327338 guard tmpFile. fileExists ( ) else {
328339 throw SwiftlyError ( message: " \( tmpFile) doesn't exist " )
329340 }
330341
331342 if !self . swiftlyToolchainsDir ( ctx) . fileExists ( ) {
332- try FileManager . default. createDirectory ( at: self . swiftlyToolchainsDir ( ctx) , withIntermediateDirectories: false )
343+ try FileManager . default. createDirectory (
344+ at: self . swiftlyToolchainsDir ( ctx) , withIntermediateDirectories: false
345+ )
333346 }
334347
335- ctx. print ( " Extracting toolchain... " )
348+ await ctx. print ( " Extracting toolchain... " )
336349 let toolchainDir = self . swiftlyToolchainsDir ( ctx) . appendingPathComponent ( version. name)
337350
338351 if toolchainDir. fileExists ( ) {
@@ -347,15 +360,18 @@ public struct Linux: Platform {
347360 let destination = toolchainDir. appendingPathComponent ( String ( relativePath) )
348361
349362 if verbose {
350- ctx. print ( " \( destination. path) " )
363+ // To avoid having to make extractArchive async this is a regular print
364+ // to stdout. Note that it is unlikely that the test mocking will require
365+ // capturing this output.
366+ print ( " \( destination. path) " )
351367 }
352368
353369 // prepend /path/to/swiftlyHomeDir/toolchains/<toolchain> to each file name
354370 return destination
355371 }
356372 }
357373
358- public func extractSwiftlyAndInstall( _ ctx: SwiftlyCoreContext , from archive: URL ) throws {
374+ public func extractSwiftlyAndInstall( _ ctx: SwiftlyCoreContext , from archive: URL ) async throws {
359375 guard archive. fileExists ( ) else {
360376 throw SwiftlyError ( message: " \( archive) doesn't exist " )
361377 }
@@ -366,7 +382,7 @@ public struct Linux: Platform {
366382 }
367383 try FileManager . default. createDirectory ( atPath: tmpDir. path, withIntermediateDirectories: true )
368384
369- ctx. print ( " Extracting new swiftly... " )
385+ await ctx. print ( " Extracting new swiftly... " )
370386 try extractArchive ( atPath: archive) { name in
371387 // Extract to the temporary directory
372388 tmpDir. appendingPathComponent ( String ( name) )
@@ -375,7 +391,9 @@ public struct Linux: Platform {
375391 try self . runProgram ( tmpDir. appendingPathComponent ( " swiftly " ) . path, " init " )
376392 }
377393
378- public func uninstall( _ ctx: SwiftlyCoreContext , _ toolchain: ToolchainVersion , verbose _: Bool ) throws {
394+ public func uninstall( _ ctx: SwiftlyCoreContext , _ toolchain: ToolchainVersion , verbose _: Bool )
395+ throws
396+ {
379397 let toolchainDir = self . swiftlyToolchainsDir ( ctx) . appendingPathComponent ( toolchain. name)
380398 try FileManager . default. removeItem ( at: toolchainDir)
381399 }
@@ -390,9 +408,11 @@ public struct Linux: Platform {
390408 FileManager . default. temporaryDirectory. appendingPathComponent ( " swiftly- \( UUID ( ) ) " )
391409 }
392410
393- public func verifySignature( _ ctx: SwiftlyCoreContext , archiveDownloadURL: URL , archive: URL , verbose: Bool ) async throws {
411+ public func verifyToolchainSignature(
412+ _ ctx: SwiftlyCoreContext , toolchainFile: ToolchainFile , archive: URL , verbose: Bool
413+ ) async throws {
394414 if verbose {
395- ctx. print ( " Downloading toolchain signature... " )
415+ await ctx. print ( " Downloading toolchain signature... " )
396416 }
397417
398418 let sigFile = self . getTempFilePath ( )
@@ -401,15 +421,15 @@ public struct Linux: Platform {
401421 try ? FileManager . default. removeItem ( at: sigFile)
402422 }
403423
404- try await ctx. httpClient. downloadFile (
405- url: archiveDownloadURL. appendingPathExtension ( " sig " ) ,
406- to: sigFile
407- )
424+ try await ctx. httpClient. getSwiftToolchainFileSignature ( toolchainFile) . download ( to: sigFile)
408425
409- ctx. print ( " Verifying toolchain signature... " )
426+ await ctx. print ( " Verifying toolchain signature... " )
410427 do {
411428 if let mockedHomeDir = ctx. mockedHomeDir {
412- try self . runProgram ( " gpg " , " --verify " , sigFile. path, archive. path, quiet: false , env: [ " GNUPGHOME " : mockedHomeDir. appendingPathComponent ( " .gnupg " ) . path] )
429+ try self . runProgram (
430+ " gpg " , " --verify " , sigFile. path, archive. path, quiet: false ,
431+ env: [ " GNUPGHOME " : mockedHomeDir. appendingPathComponent ( " .gnupg " ) . path]
432+ )
413433 } else {
414434 try self . runProgram ( " gpg " , " --verify " , sigFile. path, archive. path, quiet: !verbose)
415435 }
@@ -418,23 +438,65 @@ public struct Linux: Platform {
418438 }
419439 }
420440
421- private func manualSelectPlatform( _ ctx: SwiftlyCoreContext , _ platformPretty: String ? ) async -> PlatformDefinition {
441+ public func verifySwiftlySignature(
442+ _ ctx: SwiftlyCoreContext , archiveDownloadURL: URL , archive: URL , verbose: Bool
443+ ) async throws {
444+ if verbose {
445+ await ctx. print ( " Downloading swiftly signature... " )
446+ }
447+
448+ let sigFile = self . getTempFilePath ( )
449+ let _ = FileManager . default. createFile ( atPath: sigFile. path, contents: nil )
450+ defer {
451+ try ? FileManager . default. removeItem ( at: sigFile)
452+ }
453+
454+ try await ctx. httpClient. getSwiftlyReleaseSignature (
455+ url: archiveDownloadURL. appendingPathExtension ( " sig " )
456+ ) . download ( to: sigFile)
457+
458+ await ctx. print ( " Verifying swiftly signature... " )
459+ do {
460+ if let mockedHomeDir = ctx. mockedHomeDir {
461+ try self . runProgram (
462+ " gpg " , " --verify " , sigFile. path, archive. path, quiet: false ,
463+ env: [ " GNUPGHOME " : mockedHomeDir. appendingPathComponent ( " .gnupg " ) . path]
464+ )
465+ } else {
466+ try self . runProgram ( " gpg " , " --verify " , sigFile. path, archive. path, quiet: !verbose)
467+ }
468+ } catch {
469+ throw SwiftlyError ( message: " Signature verification failed: \( error) . " )
470+ }
471+ }
472+
473+ private func manualSelectPlatform( _ ctx: SwiftlyCoreContext , _ platformPretty: String ? ) async
474+ -> PlatformDefinition
475+ {
422476 if let platformPretty {
423- print ( " \( platformPretty) is not an officially supported platform, but the toolchains for another platform may still work on it. " )
477+ print (
478+ " \( platformPretty) is not an officially supported platform, but the toolchains for another platform may still work on it. "
479+ )
424480 } else {
425- print ( " This platform could not be detected, but a toolchain for one of the supported platforms may work on it. " )
481+ print (
482+ " This platform could not be detected, but a toolchain for one of the supported platforms may work on it. "
483+ )
426484 }
427485
428- let selections = self . linuxPlatforms. enumerated ( ) . map { " \( $0 + 1 ) ) \( $1. namePretty) " } . joined ( separator: " \n " )
486+ let selections = self . linuxPlatforms. enumerated ( ) . map { " \( $0 + 1 ) ) \( $1. namePretty) " } . joined (
487+ separator: " \n " )
429488
430- print ( """
431- Please select the platform to use for toolchain downloads:
489+ print (
490+ """
491+ Please select the platform to use for toolchain downloads:
432492
433- 0) Cancel
434- \( selections)
435- """ )
493+ 0) Cancel
494+ \( selections)
495+ """ )
436496
437- let choice = ctx. readLine ( prompt: " Pick one of the available selections [0- \( self . linuxPlatforms. count) ] " ) ?? " 0 "
497+ let choice =
498+ await ctx. readLine ( prompt: " Pick one of the available selections [0- \( self . linuxPlatforms. count) ] " )
499+ ?? " 0 "
438500
439501 guard let choiceNum = Int ( choice) else {
440502 fatalError ( " Installation canceled " )
@@ -447,11 +509,15 @@ public struct Linux: Platform {
447509 return self . linuxPlatforms [ choiceNum - 1 ]
448510 }
449511
450- public func detectPlatform( _ ctx: SwiftlyCoreContext , disableConfirmation: Bool , platform: String ? ) async throws -> PlatformDefinition {
512+ public func detectPlatform(
513+ _ ctx: SwiftlyCoreContext , disableConfirmation: Bool , platform: String ?
514+ ) async throws -> PlatformDefinition {
451515 // We've been given a hint to use
452516 if let platform {
453517 guard let pd = linuxPlatforms. first ( where: { $0. nameFull == platform } ) else {
454- fatalError ( " Unrecognized platform \( platform) . Recognized values: \( self . linuxPlatforms. map ( \. nameFull) . joined ( separator: " , " ) ) . " )
518+ fatalError (
519+ " Unrecognized platform \( platform) . Recognized values: \( self . linuxPlatforms. map ( \. nameFull) . joined ( separator: " , " ) ) . "
520+ )
455521 }
456522
457523 return pd
@@ -489,9 +555,13 @@ public struct Linux: Platform {
489555 } else if info. hasPrefix ( " ID_LIKE= " ) {
490556 idlike = String ( info. dropFirst ( " ID_LIKE= " . count) ) . replacingOccurrences ( of: " \" " , with: " " )
491557 } else if info. hasPrefix ( " VERSION_ID= " ) {
492- versionID = String ( info. dropFirst ( " VERSION_ID= " . count) ) . replacingOccurrences ( of: " \" " , with: " " ) . replacingOccurrences ( of: " . " , with: " " )
558+ versionID = String ( info. dropFirst ( " VERSION_ID= " . count) ) . replacingOccurrences (
559+ of: " \" " , with: " "
560+ ) . replacingOccurrences ( of: " . " , with: " " )
493561 } else if info. hasPrefix ( " PRETTY_NAME= " ) {
494- platformPretty = String ( info. dropFirst ( " PRETTY_NAME= " . count) ) . replacingOccurrences ( of: " \" " , with: " " )
562+ platformPretty = String ( info. dropFirst ( " PRETTY_NAME= " . count) ) . replacingOccurrences (
563+ of: " \" " , with: " "
564+ )
495565 }
496566 }
497567
@@ -529,7 +599,9 @@ public struct Linux: Platform {
529599 }
530600
531601 return . rhel9
532- } else if let pd = [ PlatformDefinition . ubuntu1804, . ubuntu2004, . ubuntu2204, . ubuntu2404, . debian12, . fedora39] . first ( where: { $0. name == id + versionID } ) {
602+ } else if let pd = [
603+ PlatformDefinition . ubuntu1804, . ubuntu2004, . ubuntu2204, . ubuntu2404, . debian12, . fedora39,
604+ ] . first ( where: { $0. name == id + versionID } ) {
533605 return pd
534606 }
535607
@@ -559,7 +631,8 @@ public struct Linux: Platform {
559631 return " /bin/bash"
560632 }
561633
562- public func findToolchainLocation( _ ctx: SwiftlyCoreContext, _ toolchain: ToolchainVersion) - > URL {
634+ public func findToolchainLocation( _ ctx: SwiftlyCoreContext, _ toolchain: ToolchainVersion) - > URL
635+ {
563636 self . swiftlyToolchainsDir ( ctx) . appendingPathComponent ( " \( toolchain. name) " )
564637 }
565638
0 commit comments