Skip to content

Commit 76ba540

Browse files
committed
Streamline passing of test contexts to common Swiftly API
Remove extraneous HTTP requests during most testing Fully sandbox and mock GPG key import and verification
1 parent 29f6b4b commit 76ba540

23 files changed

+261
-270
lines changed

Sources/LinuxPlatform/Linux.swift

Lines changed: 24 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
import Foundation
22
import 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
@@ -69,7 +67,7 @@ public struct Linux: Platform {
6967
}
7068
}
7169

72-
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? {
7371
// TODO: these are hard-coded until we have a place to query for these based on the toolchain version
7472
// These lists were copied from the dockerfile sources here: https://github.com/apple/swift-docker/tree/ea035798755cce4ec41e0c6dbdd320904cef0421/5.10
7573
let packages: [String] = switch platformName {
@@ -260,22 +258,21 @@ public struct Linux: Platform {
260258
throw SwiftlyError(message: msg)
261259
}
262260

263-
// Import the latest swift keys, but only once per session, which will help with the performance in tests
264-
if !swiftGPGKeysRefreshed {
265-
let tmpFile = self.getTempFilePath()
266-
let _ = FileManager.default.createFile(atPath: tmpFile.path, contents: nil, attributes: [.posixPermissions: 0o600])
267-
defer {
268-
try? FileManager.default.removeItem(at: tmpFile)
269-
}
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+
}
270266

271-
guard let url = URL(string: "https://www.swift.org/keys/all-keys.asc") else {
272-
throw SwiftlyError(message: "malformed URL to the swift gpg keys")
273-
}
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+
}
274270

275-
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 {
276275
try self.runProgram("gpg", "--import", tmpFile.path, quiet: true)
277-
278-
swiftGPGKeysRefreshed = true
279276
}
280277
}
281278

@@ -335,7 +332,7 @@ public struct Linux: Platform {
335332
try FileManager.default.createDirectory(at: self.swiftlyToolchainsDir(ctx), withIntermediateDirectories: false)
336333
}
337334

338-
SwiftlyCore.print(ctx, "Extracting toolchain...")
335+
ctx.print("Extracting toolchain...")
339336
let toolchainDir = self.swiftlyToolchainsDir(ctx).appendingPathComponent(version.name)
340337

341338
if toolchainDir.fileExists() {
@@ -350,7 +347,7 @@ public struct Linux: Platform {
350347
let destination = toolchainDir.appendingPathComponent(String(relativePath))
351348

352349
if verbose {
353-
SwiftlyCore.print(ctx, "\(destination.path)")
350+
ctx.print("\(destination.path)")
354351
}
355352

356353
// prepend /path/to/swiftlyHomeDir/toolchains/<toolchain> to each file name
@@ -366,7 +363,7 @@ public struct Linux: Platform {
366363
let tmpDir = self.getTempFilePath()
367364
try FileManager.default.createDirectory(atPath: tmpDir.path, withIntermediateDirectories: true)
368365

369-
SwiftlyCore.print(ctx, "Extracting new swiftly...")
366+
ctx.print("Extracting new swiftly...")
370367
try extractArchive(atPath: archive) { name in
371368
// Extract to the temporary directory
372369
tmpDir.appendingPathComponent(String(name))
@@ -392,7 +389,7 @@ public struct Linux: Platform {
392389

393390
public func verifySignature(_ ctx: SwiftlyCoreContext, archiveDownloadURL: URL, archive: URL, verbose: Bool) async throws {
394391
if verbose {
395-
SwiftlyCore.print(ctx, "Downloading toolchain signature...")
392+
ctx.print("Downloading toolchain signature...")
396393
}
397394

398395
let sigFile = self.getTempFilePath()
@@ -406,9 +403,13 @@ public struct Linux: Platform {
406403
to: sigFile
407404
)
408405

409-
SwiftlyCore.print(ctx, "Verifying toolchain signature...")
406+
ctx.print("Verifying toolchain signature...")
410407
do {
411-
try self.runProgram("gpg", "--verify", sigFile.path, archive.path, quiet: !verbose)
408+
if let mockedHomeDir = ctx.mockedHomeDir {
409+
try self.runProgram("gpg", "--verify", sigFile.path, archive.path, quiet: false, env: ["GNUPGHOME": mockedHomeDir.appendingPathComponent(".gnupg").path])
410+
} else {
411+
try self.runProgram("gpg", "--verify", sigFile.path, archive.path, quiet: !verbose)
412+
}
412413
} catch {
413414
throw SwiftlyError(message: "Signature verification failed: \(error).")
414415
}
@@ -430,7 +431,7 @@ public struct Linux: Platform {
430431
\(selections)
431432
""")
432433

433-
let choice = SwiftlyCore.readLine(ctx, prompt: "Pick one of the available selections [0-\(self.linuxPlatforms.count)] ") ?? "0"
434+
let choice = ctx.readLine(prompt: "Pick one of the available selections [0-\(self.linuxPlatforms.count)] ") ?? "0"
434435

435436
guard let choiceNum = Int(choice) else {
436437
fatalError("Installation canceled")

Sources/MacOSPlatform/MacOS.swift

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ public struct MacOS: Platform {
3939
// All system prerequisites are there for swiftly on macOS
4040
}
4141

42-
public func verifySystemPrerequisitesForInstall(httpClient _: SwiftlyHTTPClient, platformName _: String, version _: ToolchainVersion, requireSignatureValidation _: Bool) async throws -> String? {
42+
public func verifySystemPrerequisitesForInstall(_: SwiftlyCoreContext, platformName _: String, version _: ToolchainVersion, requireSignatureValidation _: Bool) async throws -> String? {
4343
// All system prerequisites should be there for macOS
4444
nil
4545
}
@@ -54,12 +54,12 @@ public struct MacOS: Platform {
5454
}
5555

5656
if ctx.mockedHomeDir == nil {
57-
SwiftlyCore.print(ctx, "Installing package in user home directory...")
57+
ctx.print("Installing package in user home directory...")
5858
try runProgram("installer", "-verbose", "-pkg", tmpFile.path, "-target", "CurrentUserHomeDirectory", quiet: !verbose)
5959
} else {
6060
// In the case of a mock for testing purposes we won't use the installer, perferring a manual process because
6161
// the installer will not install to an arbitrary path, only a volume or user home directory.
62-
SwiftlyCore.print(ctx, "Expanding pkg...")
62+
ctx.print("Expanding pkg...")
6363
let tmpDir = self.getTempFilePath()
6464
let toolchainDir = self.swiftlyToolchainsDir(ctx).appendingPathComponent("\(version.identifier).xctoolchain", isDirectory: true)
6565
if !toolchainDir.fileExists() {
@@ -73,7 +73,7 @@ public struct MacOS: Platform {
7373
payload = tmpDir.appendingPathComponent("\(version.identifier)-osx-package.pkg/Payload")
7474
}
7575

76-
SwiftlyCore.print(ctx, "Untarring pkg Payload...")
76+
ctx.print("Untarring pkg Payload...")
7777
try runProgram("tar", "-C", toolchainDir.path, "-xvf", payload.path, quiet: !verbose)
7878
}
7979
}
@@ -88,7 +88,7 @@ public struct MacOS: Platform {
8888
if ctx.mockedHomeDir == nil {
8989
homeDir = FileManager.default.homeDirectoryForCurrentUser
9090

91-
SwiftlyCore.print(ctx, "Extracting the swiftly package...")
91+
ctx.print("Extracting the swiftly package...")
9292
try runProgram("installer", "-pkg", archive.path, "-target", "CurrentUserHomeDirectory")
9393
try? runProgram("pkgutil", "--volume", homeDir.path, "--forget", "org.swift.swiftly")
9494
} else {
@@ -109,15 +109,15 @@ public struct MacOS: Platform {
109109
throw SwiftlyError(message: "Payload file could not be found at \(tmpDir).")
110110
}
111111

112-
SwiftlyCore.print(ctx, "Extracting the swiftly package into \(installDir.path)...")
112+
ctx.print("Extracting the swiftly package into \(installDir.path)...")
113113
try runProgram("tar", "-C", installDir.path, "-xvf", payload.path, quiet: false)
114114
}
115115

116116
try self.runProgram(homeDir.appendingPathComponent(".swiftly/bin/swiftly").path, "init")
117117
}
118118

119119
public func uninstall(_ ctx: SwiftlyCoreContext, _ toolchain: ToolchainVersion, verbose: Bool) throws {
120-
SwiftlyCore.print(ctx, "Uninstalling package in user home directory...")
120+
ctx.print("Uninstalling package in user home directory...")
121121

122122
let toolchainDir = self.swiftlyToolchainsDir(ctx).appendingPathComponent("\(toolchain.identifier).xctoolchain", isDirectory: true)
123123

Sources/Swiftly/Init.swift

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -113,9 +113,9 @@ struct Init: SwiftlyCommand {
113113
"""
114114
}
115115

116-
SwiftlyCore.print(ctx, msg)
116+
ctx.print(msg)
117117

118-
guard SwiftlyCore.promptForConfirmation(ctx, defaultBehavior: true) else {
118+
guard ctx.promptForConfirmation(defaultBehavior: true) else {
119119
throw SwiftlyError(message: "swiftly installation has been cancelled")
120120
}
121121
}
@@ -180,7 +180,7 @@ struct Init: SwiftlyCommand {
180180
try Swiftly.currentPlatform.installSwiftlyBin(ctx)
181181

182182
if overwrite || !FileManager.default.fileExists(atPath: envFile.path) {
183-
SwiftlyCore.print(ctx, "Creating shell environment file for the user...")
183+
ctx.print("Creating shell environment file for the user...")
184184
var env = ""
185185
if shell.hasSuffix("fish") {
186186
env = """
@@ -206,7 +206,7 @@ struct Init: SwiftlyCommand {
206206
}
207207

208208
if !noModifyProfile {
209-
SwiftlyCore.print(ctx, "Updating profile...")
209+
ctx.print("Updating profile...")
210210

211211
let userHome = ctx.mockedHomeDir ?? FileManager.default.homeDirectoryForCurrentUser
212212

@@ -262,7 +262,7 @@ struct Init: SwiftlyCommand {
262262
try Data(sourceLine.utf8).append(to: profileHome)
263263

264264
if !quietShellFollowup {
265-
SwiftlyCore.print(ctx, """
265+
ctx.print("""
266266
To begin using installed swiftly from your current shell, first run the following command:
267267
\(sourceLine)
268268
@@ -272,7 +272,7 @@ struct Init: SwiftlyCommand {
272272

273273
// Fish doesn't have path caching, so this might only be needed for bash/zsh
274274
if pathChanged && !quietShellFollowup && !shell.hasSuffix("fish") {
275-
SwiftlyCore.print(ctx, """
275+
ctx.print("""
276276
Your shell caches items on your path for better performance. Swiftly has added
277277
items to your path that may not get picked up right away. You can update your
278278
shell's environment by running
@@ -285,7 +285,7 @@ struct Init: SwiftlyCommand {
285285
}
286286

287287
if let postInstall {
288-
SwiftlyCore.print(ctx, """
288+
ctx.print("""
289289
There are some dependencies that should be installed before using this toolchain.
290290
You can run the following script as the system administrator (e.g. root) to prepare
291291
your system:

Sources/Swiftly/Install.swift

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ struct Install: SwiftlyCommand {
116116

117117
// Fish doesn't cache its path, so this instruction is not necessary.
118118
if pathChanged && !shell.hasSuffix("fish") {
119-
SwiftlyCore.print(ctx, """
119+
ctx.print("""
120120
NOTE: Swiftly has updated some elements in your path and your shell may not yet be
121121
aware of the changes. You can update your shell's environment by running
122122
@@ -153,16 +153,16 @@ struct Install: SwiftlyCommand {
153153
assumeYes: Bool
154154
) async throws -> (postInstall: String?, pathChanged: Bool) {
155155
guard !config.installedToolchains.contains(version) else {
156-
SwiftlyCore.print(ctx, "\(version) is already installed.")
156+
ctx.print("\(version) is already installed.")
157157
return (nil, false)
158158
}
159159

160160
// Ensure the system is set up correctly before downloading it. Problems that prevent installation
161161
// will throw, while problems that prevent use of the toolchain will be written out as a post install
162162
// script for the user to run afterwards.
163-
let postInstallScript = try await Swiftly.currentPlatform.verifySystemPrerequisitesForInstall(httpClient: ctx.httpClient, platformName: config.platform.name, version: version, requireSignatureValidation: verifySignature)
163+
let postInstallScript = try await Swiftly.currentPlatform.verifySystemPrerequisitesForInstall(ctx, platformName: config.platform.name, version: version, requireSignatureValidation: verifySignature)
164164

165-
SwiftlyCore.print(ctx, "Installing \(version)")
165+
ctx.print("Installing \(version)")
166166

167167
let tmpFile = Swiftly.currentPlatform.getTempFilePath()
168168
FileManager.default.createFile(atPath: tmpFile.path, contents: nil)
@@ -275,19 +275,19 @@ struct Install: SwiftlyCommand {
275275

276276
let overwrite = Set(toolchainBinDirContents).subtracting(existingProxies).intersection(swiftlyBinDirContents)
277277
if !overwrite.isEmpty && !assumeYes {
278-
SwiftlyCore.print(ctx, "The following existing executables will be overwritten:")
278+
ctx.print("The following existing executables will be overwritten:")
279279

280280
for executable in overwrite {
281-
SwiftlyCore.print(ctx, " \(swiftlyBinDir.appendingPathComponent(executable).path)")
281+
ctx.print(" \(swiftlyBinDir.appendingPathComponent(executable).path)")
282282
}
283283

284-
guard SwiftlyCore.promptForConfirmation(ctx, defaultBehavior: false) else {
284+
guard ctx.promptForConfirmation(defaultBehavior: false) else {
285285
throw SwiftlyError(message: "Toolchain installation has been cancelled")
286286
}
287287
}
288288

289289
if verbose {
290-
SwiftlyCore.print(ctx, "Setting up toolchain proxies...")
290+
ctx.print("Setting up toolchain proxies...")
291291
}
292292

293293
let proxiesToCreate = Set(toolchainBinDirContents).subtracting(swiftlyBinDirContents).union(overwrite)
@@ -323,18 +323,18 @@ struct Install: SwiftlyCommand {
323323
if config.inUse == nil {
324324
config.inUse = version
325325
try config.save(ctx)
326-
SwiftlyCore.print(ctx, "The global default toolchain has been set to `\(version)`")
326+
ctx.print("The global default toolchain has been set to `\(version)`")
327327
}
328328

329-
SwiftlyCore.print(ctx, "\(version) installed successfully!")
329+
ctx.print("\(version) installed successfully!")
330330
return (postInstallScript, pathChanged)
331331
}
332332

333333
/// Utilize the swift.org API along with the provided selector to select a toolchain for install.
334334
public static func resolve(_ ctx: SwiftlyCoreContext, config: Config, selector: ToolchainSelector) async throws -> ToolchainVersion {
335335
switch selector {
336336
case .latest:
337-
SwiftlyCore.print(ctx, "Fetching the latest stable Swift release...")
337+
ctx.print("Fetching the latest stable Swift release...")
338338

339339
guard let release = try await ctx.httpClient.getReleaseToolchains(platform: config.platform, limit: 1).first else {
340340
throw SwiftlyError(message: "couldn't get latest releases")
@@ -352,7 +352,7 @@ struct Install: SwiftlyCommand {
352352
return .stable(ToolchainVersion.StableRelease(major: major, minor: minor, patch: patch))
353353
}
354354

355-
SwiftlyCore.print(ctx, "Fetching the latest stable Swift \(major).\(minor) release...")
355+
ctx.print("Fetching the latest stable Swift \(major).\(minor) release...")
356356
// If a patch was not provided, perform a lookup to get the latest patch release
357357
// of the provided major/minor version pair.
358358
let toolchain = try await ctx.httpClient.getReleaseToolchains(platform: config.platform, limit: 1) { release in
@@ -370,7 +370,7 @@ struct Install: SwiftlyCommand {
370370
return ToolchainVersion(snapshotBranch: branch, date: date)
371371
}
372372

373-
SwiftlyCore.print(ctx, "Fetching the latest \(branch) branch snapshot...")
373+
ctx.print("Fetching the latest \(branch) branch snapshot...")
374374

375375
// If a date was not provided, perform a lookup to find the most recent snapshot
376376
// for the given branch.

Sources/Swiftly/List.swift

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ struct List: SwiftlyCommand {
5656
if toolchain == config.inUse {
5757
message += " (default)"
5858
}
59-
SwiftlyCore.print(ctx, message)
59+
ctx.print(message)
6060
}
6161

6262
if let selector {
@@ -76,24 +76,24 @@ struct List: SwiftlyCommand {
7676
}
7777

7878
let message = "Installed \(modifier) toolchains"
79-
SwiftlyCore.print(ctx, message)
80-
SwiftlyCore.print(ctx, String(repeating: "-", count: message.count))
79+
ctx.print(message)
80+
ctx.print(String(repeating: "-", count: message.count))
8181
for toolchain in toolchains {
8282
printToolchain(toolchain)
8383
}
8484
} else {
85-
SwiftlyCore.print(ctx, "Installed release toolchains")
86-
SwiftlyCore.print(ctx, "----------------------------")
85+
ctx.print("Installed release toolchains")
86+
ctx.print("----------------------------")
8787
for toolchain in toolchains {
8888
guard toolchain.isStableRelease() else {
8989
continue
9090
}
9191
printToolchain(toolchain)
9292
}
9393

94-
SwiftlyCore.print(ctx, "")
95-
SwiftlyCore.print(ctx, "Installed snapshot toolchains")
96-
SwiftlyCore.print(ctx, "-----------------------------")
94+
ctx.print("")
95+
ctx.print("Installed snapshot toolchains")
96+
ctx.print("-----------------------------")
9797
for toolchain in toolchains where toolchain.isSnapshot() {
9898
printToolchain(toolchain)
9999
}

Sources/Swiftly/ListAvailable.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ struct ListAvailable: SwiftlyCommand {
8080
} else if installedToolchains.contains(toolchain) {
8181
message += " (installed)"
8282
}
83-
SwiftlyCore.print(ctx, message)
83+
ctx.print(message)
8484
}
8585

8686
if let selector {
@@ -100,8 +100,8 @@ struct ListAvailable: SwiftlyCommand {
100100
}
101101

102102
let message = "Available \(modifier) toolchains"
103-
SwiftlyCore.print(ctx, message)
104-
SwiftlyCore.print(ctx, String(repeating: "-", count: message.count))
103+
ctx.print(message)
104+
ctx.print(String(repeating: "-", count: message.count))
105105
for toolchain in toolchains {
106106
printToolchain(toolchain)
107107
}

0 commit comments

Comments
 (0)