11import Foundation
22import SwiftlyCore
33
4- var swiftGPGKeysRefreshed = false
5-
64/// `Platform` implementation for Linux systems.
75/// This implementation can be reused for any supported Linux platform.
86/// TODO: replace dummy implementations
@@ -29,25 +27,21 @@ public struct Linux: Platform {
2927 }
3028 }
3129
32- public var swiftlyBinDir : URL {
33- SwiftlyCore . mockedHomeDir. map { $0. appendingPathComponent ( " bin " , isDirectory: true ) }
30+ public func swiftlyBinDir( _ ctx : SwiftlyCoreContext ) -> URL {
31+ ctx . mockedHomeDir. map { $0. appendingPathComponent ( " bin " , isDirectory: true ) }
3432 ?? ProcessInfo . processInfo. environment [ " SWIFTLY_BIN_DIR " ] . map { URL ( fileURLWithPath: $0) }
3533 ?? FileManager . default. homeDirectoryForCurrentUser
3634 . appendingPathComponent ( " .local/share/swiftly/bin " , isDirectory: true )
3735 }
3836
39- public var swiftlyToolchainsDir : URL {
40- self . swiftlyHomeDir. appendingPathComponent ( " toolchains " , isDirectory: true )
37+ public func swiftlyToolchainsDir( _ ctx : SwiftlyCoreContext ) -> URL {
38+ self . swiftlyHomeDir ( ctx ) . appendingPathComponent ( " toolchains " , isDirectory: true )
4139 }
4240
4341 public var toolchainFileExtension : String {
4442 " tar.gz "
4543 }
4644
47- public func isSystemDependencyPresent( _: SystemDependency ) -> Bool {
48- true
49- }
50-
5145 private static let skipVerificationMessage : String = " To skip signature verification, specify the --no-verify flag. "
5246
5347 public func verifySwiftlySystemPrerequisites( ) throws {
@@ -73,7 +67,7 @@ public struct Linux: Platform {
7367 }
7468 }
7569
76- public func verifySystemPrerequisitesForInstall( httpClient : SwiftlyHTTPClient , platformName: String , version _: ToolchainVersion , requireSignatureValidation: Bool ) async throws -> String ? {
70+ public func verifySystemPrerequisitesForInstall( _ ctx : SwiftlyCoreContext , platformName: String , version _: ToolchainVersion , requireSignatureValidation: Bool ) async throws -> String ? {
7771 // TODO: these are hard-coded until we have a place to query for these based on the toolchain version
7872 // These lists were copied from the dockerfile sources here: https://github.com/apple/swift-docker/tree/ea035798755cce4ec41e0c6dbdd320904cef0421/5.10
7973 let packages : [ String ] = switch platformName {
@@ -264,22 +258,21 @@ public struct Linux: Platform {
264258 throw SwiftlyError ( message: msg)
265259 }
266260
267- // Import the latest swift keys, but only once per session, which will help with the performance in tests
268- if !swiftGPGKeysRefreshed {
269- let tmpFile = self . getTempFilePath ( )
270- let _ = FileManager . default. createFile ( atPath: tmpFile. path, contents: nil , attributes: [ . posixPermissions: 0o600 ] )
271- defer {
272- try ? FileManager . default. removeItem ( at: tmpFile)
273- }
261+ let tmpFile = self . getTempFilePath ( )
262+ let _ = FileManager . default. createFile ( atPath: tmpFile. path, contents: nil , attributes: [ . posixPermissions: 0o600 ] )
263+ defer {
264+ try ? FileManager . default. removeItem ( at: tmpFile)
265+ }
274266
275- guard let url = URL ( string: " https://www.swift.org/keys/all-keys.asc " ) else {
276- throw SwiftlyError ( message: " malformed URL to the swift gpg keys " )
277- }
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+ }
278270
279- try await httpClient. downloadFile ( url: url, to: tmpFile)
271+ try await ctx. httpClient. downloadFile ( url: url, to: tmpFile)
272+ if let mockedHomeDir = ctx. mockedHomeDir {
273+ try self . runProgram ( " gpg " , " --import " , tmpFile. path, quiet: true , env: [ " GNUPGHOME " : mockedHomeDir. appendingPathComponent ( " .gnupg " ) . path] )
274+ } else {
280275 try self . runProgram ( " gpg " , " --import " , tmpFile. path, quiet: true )
281-
282- swiftGPGKeysRefreshed = true
283276 }
284277 }
285278
@@ -330,17 +323,17 @@ public struct Linux: Platform {
330323 }
331324 }
332325
333- public func install( from tmpFile: URL , version: ToolchainVersion , verbose: Bool ) throws {
326+ public func install( _ ctx : SwiftlyCoreContext , from tmpFile: URL , version: ToolchainVersion , verbose: Bool ) throws {
334327 guard tmpFile. fileExists ( ) else {
335328 throw SwiftlyError ( message: " \( tmpFile) doesn't exist " )
336329 }
337330
338- if !self . swiftlyToolchainsDir. fileExists ( ) {
339- try FileManager . default. createDirectory ( at: self . swiftlyToolchainsDir, withIntermediateDirectories: false )
331+ if !self . swiftlyToolchainsDir ( ctx ) . fileExists ( ) {
332+ try FileManager . default. createDirectory ( at: self . swiftlyToolchainsDir ( ctx ) , withIntermediateDirectories: false )
340333 }
341334
342- SwiftlyCore . print ( " Extracting toolchain... " )
343- let toolchainDir = self . swiftlyToolchainsDir. appendingPathComponent ( version. name)
335+ ctx . print ( " Extracting toolchain... " )
336+ let toolchainDir = self . swiftlyToolchainsDir ( ctx ) . appendingPathComponent ( version. name)
344337
345338 if toolchainDir. fileExists ( ) {
346339 try FileManager . default. removeItem ( at: toolchainDir)
@@ -354,23 +347,26 @@ public struct Linux: Platform {
354347 let destination = toolchainDir. appendingPathComponent ( String ( relativePath) )
355348
356349 if verbose {
357- SwiftlyCore . print ( " \( destination. path) " )
350+ ctx . print ( " \( destination. path) " )
358351 }
359352
360353 // prepend /path/to/swiftlyHomeDir/toolchains/<toolchain> to each file name
361354 return destination
362355 }
363356 }
364357
365- public func extractSwiftlyAndInstall( from archive: URL ) throws {
358+ public func extractSwiftlyAndInstall( _ ctx : SwiftlyCoreContext , from archive: URL ) throws {
366359 guard archive. fileExists ( ) else {
367360 throw SwiftlyError ( message: " \( archive) doesn't exist " )
368361 }
369362
370363 let tmpDir = self . getTempFilePath ( )
364+ defer {
365+ try ? FileManager . default. removeItem ( at: tmpDir)
366+ }
371367 try FileManager . default. createDirectory ( atPath: tmpDir. path, withIntermediateDirectories: true )
372368
373- SwiftlyCore . print ( " Extracting new swiftly... " )
369+ ctx . print ( " Extracting new swiftly... " )
374370 try extractArchive ( atPath: archive) { name in
375371 // Extract to the temporary directory
376372 tmpDir. appendingPathComponent ( String ( name) )
@@ -379,8 +375,8 @@ public struct Linux: Platform {
379375 try self . runProgram ( tmpDir. appendingPathComponent ( " swiftly " ) . path, " init " )
380376 }
381377
382- public func uninstall( _ toolchain: ToolchainVersion , verbose _: Bool ) throws {
383- let toolchainDir = self . swiftlyToolchainsDir. appendingPathComponent ( toolchain. name)
378+ public func uninstall( _ ctx : SwiftlyCoreContext , _ toolchain: ToolchainVersion , verbose _: Bool ) throws {
379+ let toolchainDir = self . swiftlyToolchainsDir ( ctx ) . appendingPathComponent ( toolchain. name)
384380 try FileManager . default. removeItem ( at: toolchainDir)
385381 }
386382
@@ -394,9 +390,9 @@ public struct Linux: Platform {
394390 FileManager . default. temporaryDirectory. appendingPathComponent ( " swiftly- \( UUID ( ) ) " )
395391 }
396392
397- public func verifySignature( httpClient : SwiftlyHTTPClient , archiveDownloadURL: URL , archive: URL , verbose: Bool ) async throws {
393+ public func verifySignature( _ ctx : SwiftlyCoreContext , archiveDownloadURL: URL , archive: URL , verbose: Bool ) async throws {
398394 if verbose {
399- SwiftlyCore . print ( " Downloading toolchain signature... " )
395+ ctx . print ( " Downloading toolchain signature... " )
400396 }
401397
402398 let sigFile = self . getTempFilePath ( )
@@ -405,20 +401,24 @@ public struct Linux: Platform {
405401 try ? FileManager . default. removeItem ( at: sigFile)
406402 }
407403
408- try await httpClient. downloadFile (
404+ try await ctx . httpClient. downloadFile (
409405 url: archiveDownloadURL. appendingPathExtension ( " sig " ) ,
410406 to: sigFile
411407 )
412408
413- SwiftlyCore . print ( " Verifying toolchain signature... " )
409+ ctx . print ( " Verifying toolchain signature... " )
414410 do {
415- try self . runProgram ( " gpg " , " --verify " , sigFile. path, archive. path, quiet: !verbose)
411+ if let mockedHomeDir = ctx. mockedHomeDir {
412+ try self . runProgram ( " gpg " , " --verify " , sigFile. path, archive. path, quiet: false , env: [ " GNUPGHOME " : mockedHomeDir. appendingPathComponent ( " .gnupg " ) . path] )
413+ } else {
414+ try self . runProgram ( " gpg " , " --verify " , sigFile. path, archive. path, quiet: !verbose)
415+ }
416416 } catch {
417417 throw SwiftlyError ( message: " Signature verification failed: \( error) . " )
418418 }
419419 }
420420
421- private func manualSelectPlatform( _ platformPretty: String ? ) async -> PlatformDefinition {
421+ private func manualSelectPlatform( _ ctx : SwiftlyCoreContext , _ platformPretty: String ? ) async -> PlatformDefinition {
422422 if let platformPretty {
423423 print ( " \( platformPretty) is not an officially supported platform, but the toolchains for another platform may still work on it. " )
424424 } else {
@@ -434,7 +434,7 @@ public struct Linux: Platform {
434434 \( selections)
435435 """ )
436436
437- let choice = SwiftlyCore . readLine ( prompt: " Pick one of the available selections [0- \( self . linuxPlatforms. count) ] " ) ?? " 0 "
437+ let choice = ctx . readLine ( prompt: " Pick one of the available selections [0- \( self . linuxPlatforms. count) ] " ) ?? " 0 "
438438
439439 guard let choiceNum = Int ( choice) else {
440440 fatalError ( " Installation canceled " )
@@ -447,7 +447,7 @@ public struct Linux: Platform {
447447 return self . linuxPlatforms [ choiceNum - 1 ]
448448 }
449449
450- public func detectPlatform( disableConfirmation: Bool , platform: String ? ) async throws -> PlatformDefinition {
450+ public func detectPlatform( _ ctx : SwiftlyCoreContext , disableConfirmation: Bool , platform: String ? ) async throws -> PlatformDefinition {
451451 // We've been given a hint to use
452452 if let platform {
453453 guard let pd = linuxPlatforms. first ( where: { $0. nameFull == platform } ) else {
@@ -475,7 +475,7 @@ public struct Linux: Platform {
475475 } else {
476476 print ( message)
477477 }
478- return await self . manualSelectPlatform ( platformPretty)
478+ return await self . manualSelectPlatform ( ctx , platformPretty)
479479 }
480480
481481 let releaseInfo = try String ( contentsOfFile: releaseFile, encoding: . utf8)
@@ -502,7 +502,7 @@ public struct Linux: Platform {
502502 } else {
503503 print ( message)
504504 }
505- return await self . manualSelectPlatform ( platformPretty)
505+ return await self . manualSelectPlatform ( ctx , platformPretty)
506506 }
507507
508508 if ( id + ( idlike ?? " " ) ) . contains ( " amzn " ) {
@@ -513,7 +513,7 @@ public struct Linux: Platform {
513513 } else {
514514 print ( message)
515515 }
516- return await self . manualSelectPlatform ( platformPretty)
516+ return await self . manualSelectPlatform ( ctx , platformPretty)
517517 }
518518
519519 return . amazonlinux2
@@ -525,7 +525,7 @@ public struct Linux: Platform {
525525 } else {
526526 print ( message)
527527 }
528- return await self . manualSelectPlatform ( platformPretty)
528+ return await self . manualSelectPlatform ( ctx , platformPretty)
529529 }
530530
531531 return . rhel9
@@ -539,7 +539,7 @@ public struct Linux: Platform {
539539 } else {
540540 print ( message)
541541 }
542- return await self . manualSelectPlatform ( platformPretty)
542+ return await self . manualSelectPlatform ( ctx , platformPretty)
543543 }
544544
545545 public func getShell( ) async throws -> String {
@@ -559,8 +559,8 @@ public struct Linux: Platform {
559559 return " /bin/bash"
560560 }
561561
562- public func findToolchainLocation( _ toolchain: ToolchainVersion) - > URL {
563- self . swiftlyToolchainsDir. appendingPathComponent ( " \( toolchain. name) " )
562+ public func findToolchainLocation( _ ctx : SwiftlyCoreContext , _ toolchain: ToolchainVersion) - > URL {
563+ self . swiftlyToolchainsDir ( ctx ) . appendingPathComponent ( " \( toolchain. name) " )
564564 }
565565
566566 public static let currentPlatform : any Platform = Linux ( )
0 commit comments