Skip to content

Commit 6d239f4

Browse files
committed
Convert file downloading to go through the OpenAPI abstraction
This is a little awkward due to the OpenAPI spec not supporting variable length path components in an endpoint declaration (that's coming in v4 of the spec), but it does the job to get all of our HTTP traffic flowing through the same abstraction layer.
1 parent 46fb64f commit 6d239f4

File tree

9 files changed

+777
-283
lines changed

9 files changed

+777
-283
lines changed

Sources/LinuxPlatform/Linux.swift

Lines changed: 106 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -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,13 +332,17 @@ 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+
) 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

335348
ctx.print("Extracting toolchain...")
@@ -375,7 +388,9 @@ public struct Linux: Platform {
375388
try self.runProgram(tmpDir.appendingPathComponent("swiftly").path, "init")
376389
}
377390

378-
public func uninstall(_ ctx: SwiftlyCoreContext, _ toolchain: ToolchainVersion, verbose _: Bool) throws {
391+
public func uninstall(_ ctx: SwiftlyCoreContext, _ toolchain: ToolchainVersion, verbose _: Bool)
392+
throws
393+
{
379394
let toolchainDir = self.swiftlyToolchainsDir(ctx).appendingPathComponent(toolchain.name)
380395
try FileManager.default.removeItem(at: toolchainDir)
381396
}
@@ -390,7 +405,9 @@ public struct Linux: Platform {
390405
FileManager.default.temporaryDirectory.appendingPathComponent("swiftly-\(UUID())")
391406
}
392407

393-
public func verifySignature(_ ctx: SwiftlyCoreContext, archiveDownloadURL: URL, archive: URL, verbose: Bool) async throws {
408+
public func verifyToolchainSignature(
409+
_ ctx: SwiftlyCoreContext, toolchainFile: ToolchainFile, archive: URL, verbose: Bool
410+
) async throws {
394411
if verbose {
395412
ctx.print("Downloading toolchain signature...")
396413
}
@@ -401,15 +418,15 @@ public struct Linux: Platform {
401418
try? FileManager.default.removeItem(at: sigFile)
402419
}
403420

404-
try await ctx.httpClient.downloadFile(
405-
url: archiveDownloadURL.appendingPathExtension("sig"),
406-
to: sigFile
407-
)
421+
try await ctx.httpClient.getSwiftToolchainFileSignature(toolchainFile).download(to: sigFile)
408422

409423
ctx.print("Verifying toolchain signature...")
410424
do {
411425
if let mockedHomeDir = ctx.mockedHomeDir {
412-
try self.runProgram("gpg", "--verify", sigFile.path, archive.path, quiet: false, env: ["GNUPGHOME": mockedHomeDir.appendingPathComponent(".gnupg").path])
426+
try self.runProgram(
427+
"gpg", "--verify", sigFile.path, archive.path, quiet: false,
428+
env: ["GNUPGHOME": mockedHomeDir.appendingPathComponent(".gnupg").path]
429+
)
413430
} else {
414431
try self.runProgram("gpg", "--verify", sigFile.path, archive.path, quiet: !verbose)
415432
}
@@ -418,23 +435,65 @@ public struct Linux: Platform {
418435
}
419436
}
420437

421-
private func manualSelectPlatform(_ ctx: SwiftlyCoreContext, _ platformPretty: String?) async -> PlatformDefinition {
438+
public func verifySwiftlySignature(
439+
_ ctx: SwiftlyCoreContext, archiveDownloadURL: URL, archive: URL, verbose: Bool
440+
) async throws {
441+
if verbose {
442+
ctx.print("Downloading swiftly signature...")
443+
}
444+
445+
let sigFile = self.getTempFilePath()
446+
let _ = FileManager.default.createFile(atPath: sigFile.path, contents: nil)
447+
defer {
448+
try? FileManager.default.removeItem(at: sigFile)
449+
}
450+
451+
try await ctx.httpClient.getSwiftlyReleaseSignature(
452+
url: archiveDownloadURL.appendingPathExtension("sig")
453+
).download(to: sigFile)
454+
455+
ctx.print("Verifying swiftly signature...")
456+
do {
457+
if let mockedHomeDir = ctx.mockedHomeDir {
458+
try self.runProgram(
459+
"gpg", "--verify", sigFile.path, archive.path, quiet: false,
460+
env: ["GNUPGHOME": mockedHomeDir.appendingPathComponent(".gnupg").path]
461+
)
462+
} else {
463+
try self.runProgram("gpg", "--verify", sigFile.path, archive.path, quiet: !verbose)
464+
}
465+
} catch {
466+
throw SwiftlyError(message: "Signature verification failed: \(error).")
467+
}
468+
}
469+
470+
private func manualSelectPlatform(_ ctx: SwiftlyCoreContext, _ platformPretty: String?) async
471+
-> PlatformDefinition
472+
{
422473
if let platformPretty {
423-
print("\(platformPretty) is not an officially supported platform, but the toolchains for another platform may still work on it.")
474+
print(
475+
"\(platformPretty) is not an officially supported platform, but the toolchains for another platform may still work on it."
476+
)
424477
} else {
425-
print("This platform could not be detected, but a toolchain for one of the supported platforms may work on it.")
478+
print(
479+
"This platform could not be detected, but a toolchain for one of the supported platforms may work on it."
480+
)
426481
}
427482

428-
let selections = self.linuxPlatforms.enumerated().map { "\($0 + 1)) \($1.namePretty)" }.joined(separator: "\n")
483+
let selections = self.linuxPlatforms.enumerated().map { "\($0 + 1)) \($1.namePretty)" }.joined(
484+
separator: "\n")
429485

430-
print("""
431-
Please select the platform to use for toolchain downloads:
486+
print(
487+
"""
488+
Please select the platform to use for toolchain downloads:
432489
433-
0) Cancel
434-
\(selections)
435-
""")
490+
0) Cancel
491+
\(selections)
492+
""")
436493

437-
let choice = ctx.readLine(prompt: "Pick one of the available selections [0-\(self.linuxPlatforms.count)] ") ?? "0"
494+
let choice =
495+
ctx.readLine(prompt: "Pick one of the available selections [0-\(self.linuxPlatforms.count)] ")
496+
?? "0"
438497

439498
guard let choiceNum = Int(choice) else {
440499
fatalError("Installation canceled")
@@ -447,11 +506,15 @@ public struct Linux: Platform {
447506
return self.linuxPlatforms[choiceNum - 1]
448507
}
449508

450-
public func detectPlatform(_ ctx: SwiftlyCoreContext, disableConfirmation: Bool, platform: String?) async throws -> PlatformDefinition {
509+
public func detectPlatform(
510+
_ ctx: SwiftlyCoreContext, disableConfirmation: Bool, platform: String?
511+
) async throws -> PlatformDefinition {
451512
// We've been given a hint to use
452513
if let platform {
453514
guard let pd = linuxPlatforms.first(where: { $0.nameFull == platform }) else {
454-
fatalError("Unrecognized platform \(platform). Recognized values: \(self.linuxPlatforms.map(\.nameFull).joined(separator: ", ")).")
515+
fatalError(
516+
"Unrecognized platform \(platform). Recognized values: \(self.linuxPlatforms.map(\.nameFull).joined(separator: ", "))."
517+
)
455518
}
456519

457520
return pd
@@ -489,9 +552,13 @@ public struct Linux: Platform {
489552
} else if info.hasPrefix("ID_LIKE=") {
490553
idlike = String(info.dropFirst("ID_LIKE=".count)).replacingOccurrences(of: "\"", with: "")
491554
} else if info.hasPrefix("VERSION_ID=") {
492-
versionID = String(info.dropFirst("VERSION_ID=".count)).replacingOccurrences(of: "\"", with: "").replacingOccurrences(of: ".", with: "")
555+
versionID = String(info.dropFirst("VERSION_ID=".count)).replacingOccurrences(
556+
of: "\"", with: ""
557+
).replacingOccurrences(of: ".", with: "")
493558
} else if info.hasPrefix("PRETTY_NAME=") {
494-
platformPretty = String(info.dropFirst("PRETTY_NAME=".count)).replacingOccurrences(of: "\"", with: "")
559+
platformPretty = String(info.dropFirst("PRETTY_NAME=".count)).replacingOccurrences(
560+
of: "\"", with: ""
561+
)
495562
}
496563
}
497564

@@ -529,7 +596,9 @@ public struct Linux: Platform {
529596
}
530597

531598
return .rhel9
532-
} else if let pd = [PlatformDefinition.ubuntu1804, .ubuntu2004, .ubuntu2204, .ubuntu2404, .debian12, .fedora39].first(where: { $0.name == id + versionID }) {
599+
} else if let pd = [
600+
PlatformDefinition.ubuntu1804, .ubuntu2004, .ubuntu2204, .ubuntu2404, .debian12, .fedora39,
601+
].first(where: { $0.name == id + versionID }) {
533602
return pd
534603
}
535604

@@ -559,7 +628,8 @@ public struct Linux: Platform {
559628
return "/bin/bash"
560629
}
561630

562-
public func findToolchainLocation(_ ctx: SwiftlyCoreContext, _ toolchain: ToolchainVersion) -> URL {
631+
public func findToolchainLocation(_ ctx: SwiftlyCoreContext, _ toolchain: ToolchainVersion) -> URL
632+
{
563633
self.swiftlyToolchainsDir(ctx).appendingPathComponent("\(toolchain.name)")
564634
}
565635

0 commit comments

Comments
 (0)