Skip to content

Commit 3fdbb28

Browse files
committed
Adopt OpenAPI for fetching current swiftly releases
Use the OpenAPI specification for the swift.org API and the swift OpenAPI generation capabilities to avoid having to hand craft the HTTP handling and local data types.
1 parent c7fe40d commit 3fdbb28

23 files changed

+535
-125
lines changed

Package.resolved

Lines changed: 48 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Package.swift

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,12 @@ let package = Package(
1616
dependencies: [
1717
.package(url: "https://github.com/apple/swift-argument-parser", from: "1.3.0"),
1818
.package(url: "https://github.com/swift-server/async-http-client", from: "1.21.2"),
19+
.package(url: "https://github.com/swift-server/swift-openapi-async-http-client", from: "1.0.0"),
1920
.package(url: "https://github.com/apple/swift-nio.git", from: "2.64.0"),
2021
.package(url: "https://github.com/apple/swift-tools-support-core.git", from: "0.7.2"),
2122
.package(url: "https://github.com/apple/swift-docc-plugin", from: "1.3.0"),
23+
.package(url: "https://github.com/apple/swift-openapi-generator", from: "1.6.0"),
24+
.package(url: "https://github.com/apple/swift-openapi-runtime", from: "1.7.0"),
2225
// This dependency provides the correct version of the formatter so that you can run `swift run swiftformat Package.swift Plugins/ Sources/ Tests/`
2326
.package(url: "https://github.com/nicklockwood/SwiftFormat", exact: "0.49.18"),
2427
],
@@ -38,6 +41,11 @@ let package = Package(
3841
dependencies: [
3942
.product(name: "AsyncHTTPClient", package: "async-http-client"),
4043
.product(name: "NIOFoundationCompat", package: "swift-nio"),
44+
.product(name: "OpenAPIRuntime", package: "swift-openapi-runtime"),
45+
.product(name: "OpenAPIAsyncHTTPClient", package: "swift-openapi-async-http-client"),
46+
],
47+
plugins: [
48+
.plugin(name: "OpenAPIGenerator", package: "swift-openapi-generator"),
4149
]
4250
),
4351
.target(

Sources/LinuxPlatform/Linux.swift

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ public struct Linux: Platform {
6969
You can install the ca-certificates package on your system to fix this.
7070
"""
7171

72-
throw Error(message: msg)
72+
throw SwiftlyError(message: msg)
7373
}
7474
}
7575

@@ -258,7 +258,7 @@ public struct Linux: Platform {
258258
}
259259
msg += "\n" + Self.skipVerificationMessage
260260

261-
throw Error(message: msg)
261+
throw SwiftlyError(message: msg)
262262
}
263263

264264
// Import the latest swift keys, but only once per session, which will help with the performance in tests
@@ -270,7 +270,7 @@ public struct Linux: Platform {
270270
}
271271

272272
guard let url = URL(string: "https://www.swift.org/keys/all-keys.asc") else {
273-
throw Error(message: "malformed URL to the swift gpg keys")
273+
throw SwiftlyError(message: "malformed URL to the swift gpg keys")
274274
}
275275

276276
try await httpClient.downloadFile(url: url, to: tmpFile)
@@ -329,7 +329,7 @@ public struct Linux: Platform {
329329

330330
public func install(from tmpFile: URL, version: ToolchainVersion, verbose: Bool) throws {
331331
guard tmpFile.fileExists() else {
332-
throw Error(message: "\(tmpFile) doesn't exist")
332+
throw SwiftlyError(message: "\(tmpFile) doesn't exist")
333333
}
334334

335335
if !self.swiftlyToolchainsDir.fileExists() {
@@ -361,7 +361,7 @@ public struct Linux: Platform {
361361

362362
public func extractSwiftlyAndInstall(from archive: URL) throws {
363363
guard archive.fileExists() else {
364-
throw Error(message: "\(archive) doesn't exist")
364+
throw SwiftlyError(message: "\(archive) doesn't exist")
365365
}
366366

367367
let tmpDir = self.getTempFilePath()
@@ -414,7 +414,7 @@ public struct Linux: Platform {
414414
do {
415415
try self.runProgram("gpg", "--verify", sigFile.path, archive.path, quiet: !verbose)
416416
} catch {
417-
throw Error(message: "Signature verification failed: \(error).")
417+
throw SwiftlyError(message: "Signature verification failed: \(error).")
418418
}
419419
}
420420

@@ -471,7 +471,7 @@ public struct Linux: Platform {
471471
guard let releaseFile = releaseFile else {
472472
let message = "Unable to detect the type of Linux OS and the release"
473473
if disableConfirmation {
474-
throw Error(message: message)
474+
throw SwiftlyError(message: message)
475475
} else {
476476
print(message)
477477
}
@@ -498,7 +498,7 @@ public struct Linux: Platform {
498498
guard let id, let versionID else {
499499
let message = "Unable to find release information from file \(releaseFile)"
500500
if disableConfirmation {
501-
throw Error(message: message)
501+
throw SwiftlyError(message: message)
502502
} else {
503503
print(message)
504504
}
@@ -509,7 +509,7 @@ public struct Linux: Platform {
509509
guard versionID == "2" else {
510510
let message = "Unsupported version of Amazon Linux"
511511
if disableConfirmation {
512-
throw Error(message: message)
512+
throw SwiftlyError(message: message)
513513
} else {
514514
print(message)
515515
}
@@ -521,7 +521,7 @@ public struct Linux: Platform {
521521
guard versionID.hasPrefix("9") else {
522522
let message = "Unsupported version of RHEL"
523523
if disableConfirmation {
524-
throw Error(message: message)
524+
throw SwiftlyError(message: message)
525525
} else {
526526
print(message)
527527
}
@@ -535,7 +535,7 @@ public struct Linux: Platform {
535535

536536
let message = "Unsupported Linux platform"
537537
if disableConfirmation {
538-
throw Error(message: message)
538+
throw SwiftlyError(message: message)
539539
} else {
540540
print(message)
541541
}

Sources/MacOSPlatform/MacOS.swift

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ public struct MacOS: Platform {
5151

5252
public func install(from tmpFile: URL, version: ToolchainVersion, verbose: Bool) throws {
5353
guard tmpFile.fileExists() else {
54-
throw Error(message: "\(tmpFile) doesn't exist")
54+
throw SwiftlyError(message: "\(tmpFile) doesn't exist")
5555
}
5656

5757
if !self.swiftlyToolchainsDir.fileExists() {
@@ -85,7 +85,7 @@ public struct MacOS: Platform {
8585

8686
public func extractSwiftlyAndInstall(from archive: URL) throws {
8787
guard archive.fileExists() else {
88-
throw Error(message: "\(archive) doesn't exist")
88+
throw SwiftlyError(message: "\(archive) doesn't exist")
8989
}
9090

9191
let homeDir: URL
@@ -111,7 +111,7 @@ public struct MacOS: Platform {
111111
// and the ones that are mocked here in the test framework.
112112
let payload = tmpDir.appendingPathComponent("Payload")
113113
guard payload.fileExists() else {
114-
throw Error(message: "Payload file could not be found at \(tmpDir).")
114+
throw SwiftlyError(message: "Payload file could not be found at \(tmpDir).")
115115
}
116116

117117
try runProgram("tar", "-C", installDir.path, "-xf", payload.path)
@@ -128,11 +128,11 @@ public struct MacOS: Platform {
128128
let decoder = PropertyListDecoder()
129129
let infoPlist = toolchainDir.appendingPathComponent("Info.plist")
130130
guard let data = try? Data(contentsOf: infoPlist) else {
131-
throw Error(message: "could not open \(infoPlist)")
131+
throw SwiftlyError(message: "could not open \(infoPlist)")
132132
}
133133

134134
guard let pkgInfo = try? decoder.decode(SwiftPkgInfo.self, from: data) else {
135-
throw Error(message: "could not decode plist at \(infoPlist)")
135+
throw SwiftlyError(message: "could not decode plist at \(infoPlist)")
136136
}
137137

138138
try FileManager.default.removeItem(at: toolchainDir)

Sources/Swiftly/Config.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ public struct Config: Codable, Equatable {
4040
4141
To begin using swiftly you can install it: '\(CommandLine.arguments[0]) init'.
4242
"""
43-
throw Error(message: msg)
43+
throw SwiftlyError(message: msg)
4444
}
4545
}
4646

Sources/Swiftly/Init.swift

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ internal struct Init: SwiftlyCommand {
3939

4040
if let config, !overwrite && config.version != SwiftlyCore.version {
4141
// We don't support downgrades, and we don't yet support upgrades
42-
throw Error(message: "An existing swiftly installation was detected. You can try again with '--overwrite' to overwrite it.")
42+
throw SwiftlyError(message: "An existing swiftly installation was detected. You can try again with '--overwrite' to overwrite it.")
4343
}
4444

4545
// Give the user the prompt and the choice to abort at this point.
@@ -64,7 +64,7 @@ internal struct Init: SwiftlyCommand {
6464
""")
6565

6666
if SwiftlyCore.readLine(prompt: "Proceed with the installation? [Y/n] ") == "n" {
67-
throw Error(message: "Swiftly installation has been cancelled")
67+
throw SwiftlyError(message: "Swiftly installation has been cancelled")
6868
}
6969
}
7070

@@ -82,7 +82,7 @@ internal struct Init: SwiftlyCommand {
8282
let proceed = SwiftlyCore.readLine(prompt: "Proceed? [y/N]") ?? "n"
8383

8484
guard proceed == "y" else {
85-
throw Error(message: "Swiftly installation has been cancelled")
85+
throw SwiftlyError(message: "Swiftly installation has been cancelled")
8686
}
8787
}
8888

@@ -121,7 +121,7 @@ internal struct Init: SwiftlyCommand {
121121
do {
122122
try FileManager.default.createDirectory(at: requiredDir, withIntermediateDirectories: true)
123123
} catch {
124-
throw Error(message: "Failed to create required directory \"\(requiredDir.path)\": \(error)")
124+
throw SwiftlyError(message: "Failed to create required directory \"\(requiredDir.path)\": \(error)")
125125
}
126126
}
127127
}
@@ -136,7 +136,7 @@ internal struct Init: SwiftlyCommand {
136136
config = c
137137
}
138138

139-
guard var config else { throw Error(message: "Configuration could not be set") }
139+
guard var config else { throw SwiftlyError(message: "Configuration could not be set") }
140140

141141
// Move our executable over to the correct place
142142
try Swiftly.currentPlatform.installSwiftlyBin()

Sources/Swiftly/Install.swift

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -86,10 +86,10 @@ struct Install: SwiftlyCommand {
8686
} else if let error = error {
8787
throw error
8888
} else {
89-
throw Error(message: "Internal error selecting toolchain to install.")
89+
throw SwiftlyError(message: "Internal error selecting toolchain to install.")
9090
}
9191
} else {
92-
throw Error(message: "Swiftly couldn't determine the toolchain version to install. Please set a version like this and try again: `swiftly install latest`")
92+
throw SwiftlyError(message: "Swiftly couldn't determine the toolchain version to install. Please set a version like this and try again: `swiftly install latest`")
9393
}
9494
}
9595

@@ -115,7 +115,7 @@ struct Install: SwiftlyCommand {
115115

116116
if let postInstallScript {
117117
guard let postInstallFile = self.postInstallFile else {
118-
throw Error(message: """
118+
throw SwiftlyError(message: """
119119
120120
There are some dependencies that should be installed before using this toolchain.
121121
You can run the following script as the system administrator (e.g. root) to prepare
@@ -189,7 +189,7 @@ struct Install: SwiftlyCommand {
189189
url += "\(version.identifier)-\(platformFullString).\(Swiftly.currentPlatform.toolchainFileExtension)"
190190

191191
guard let url = URL(string: url) else {
192-
throw Error(message: "Invalid toolchain URL: \(url)")
192+
throw SwiftlyError(message: "Invalid toolchain URL: \(url)")
193193
}
194194

195195
let animation = PercentProgressAnimation(
@@ -223,7 +223,7 @@ struct Install: SwiftlyCommand {
223223
}
224224
)
225225
} catch let notFound as SwiftlyHTTPClient.DownloadNotFoundError {
226-
throw Error(message: "\(version) does not exist at URL \(notFound.url), exiting")
226+
throw SwiftlyError(message: "\(version) does not exist at URL \(notFound.url), exiting")
227227
} catch {
228228
animation.complete(success: false)
229229
throw error
@@ -269,7 +269,7 @@ struct Install: SwiftlyCommand {
269269
let proceed = SwiftlyCore.readLine(prompt: "Proceed? [y/N]") ?? "n"
270270

271271
guard proceed == "y" else {
272-
throw Error(message: "Toolchain installation has been cancelled")
272+
throw SwiftlyError(message: "Toolchain installation has been cancelled")
273273
}
274274
}
275275

@@ -315,13 +315,13 @@ struct Install: SwiftlyCommand {
315315
SwiftlyCore.print("Fetching the latest stable Swift release...")
316316

317317
guard let release = try await SwiftlyCore.httpClient.getReleaseToolchains(platform: config.platform, limit: 1).first else {
318-
throw Error(message: "couldn't get latest releases")
318+
throw SwiftlyError(message: "couldn't get latest releases")
319319
}
320320
return .stable(release)
321321

322322
case let .stable(major, minor, patch):
323323
guard let minor else {
324-
throw Error(
324+
throw SwiftlyError(
325325
message: "Need to provide at least major and minor versions when installing a release toolchain."
326326
)
327327
}
@@ -338,7 +338,7 @@ struct Install: SwiftlyCommand {
338338
}.first
339339

340340
guard let toolchain else {
341-
throw Error(message: "No release toolchain found matching \(major).\(minor)")
341+
throw SwiftlyError(message: "No release toolchain found matching \(major).\(minor)")
342342
}
343343

344344
return .stable(toolchain)
@@ -358,15 +358,15 @@ struct Install: SwiftlyCommand {
358358
snapshot.branch == branch
359359
}
360360
} catch let branchNotFoundErr as SwiftlyHTTPClient.SnapshotBranchNotFoundError {
361-
throw Error(message: "You have requested to install a snapshot toolchain from branch \(branchNotFoundErr.branch). It cannot be found on swift.org. Note that snapshots are only available from the current `main` release and the latest x.y (major.minor) release. Try again with a different branch.")
361+
throw SwiftlyError(message: "You have requested to install a snapshot toolchain from branch \(branchNotFoundErr.branch). It cannot be found on swift.org. Note that snapshots are only available from the current `main` release and the latest x.y (major.minor) release. Try again with a different branch.")
362362
} catch {
363363
throw error
364364
}
365365

366366
let firstSnapshot = snapshots.first
367367

368368
guard let firstSnapshot else {
369-
throw Error(message: "No snapshot toolchain found for branch \(branch)")
369+
throw SwiftlyError(message: "No snapshot toolchain found for branch \(branch)")
370370
}
371371

372372
return .snapshot(firstSnapshot)

0 commit comments

Comments
 (0)