Skip to content

Commit c0a30a3

Browse files
committed
Use OpenAPI for retrieving the snapshot releases
1 parent 8bfc70b commit c0a30a3

File tree

3 files changed

+104
-33
lines changed

3 files changed

+104
-33
lines changed

Sources/SwiftlyCore/HTTPClient.swift

Lines changed: 50 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ public protocol HTTPRequestExecutor {
5454
func execute(_ request: HTTPClientRequest, timeout: TimeAmount) async throws -> HTTPClientResponse
5555
func getCurrentSwiftlyRelease() async throws -> Components.Schemas.SwiftlyRelease
5656
func getReleaseToolchains() async throws -> [Components.Schemas.Release]
57+
func getSnapshotToolchains(branch: Components.Schemas.KnownSourceBranch, platform: Components.Schemas.KnownPlatformIdentifier) async throws -> Components.Schemas.DevToolchains
5758
}
5859

5960
internal struct SwiftlyUserAgentMiddleware: ClientMiddleware {
@@ -144,6 +145,21 @@ internal class HTTPRequestExecutorImpl: HTTPRequestExecutor {
144145

145146
return try response.ok.body.json
146147
}
148+
149+
public func getSnapshotToolchains(branch: Components.Schemas.KnownSourceBranch, platform: Components.Schemas.KnownPlatformIdentifier) async throws -> Components.Schemas.DevToolchains {
150+
let config = AsyncHTTPClientTransport.Configuration(client: self.httpClient, timeout: .seconds(30))
151+
let swiftlyUserAgent = SwiftlyUserAgentMiddleware()
152+
153+
let client = Client(
154+
serverURL: try Servers.Server1.url(),
155+
transport: AsyncHTTPClientTransport(configuration: config),
156+
middlewares: [swiftlyUserAgent]
157+
)
158+
159+
let response = try await client.listDevToolchains(.init(path: .init(branch: branch, platform: platform)))
160+
161+
return try response.ok.body.json
162+
}
147163
}
148164

149165
private func makeRequest(url: String) -> HTTPClientRequest {
@@ -218,15 +234,7 @@ extension Components.Schemas.Platform {
218234
}
219235
}
220236

221-
public struct SwiftOrgSnapshotList: Codable {
222-
var aarch64: [SwiftOrgSnapshot]?
223-
var x86_64: [SwiftOrgSnapshot]?
224-
var universal: [SwiftOrgSnapshot]?
225-
}
226-
227-
public struct SwiftOrgSnapshot: Codable {
228-
var dir: String
229-
237+
extension Components.Schemas.DevToolchainForArch {
230238
private static let snapshotRegex: Regex<(Substring, Substring?, Substring?, Substring)> =
231239
try! Regex("swift(?:-(\\d+)\\.(\\d+))?-DEVELOPMENT-SNAPSHOT-(\\d{4}-\\d{2}-\\d{2})")
232240

@@ -363,35 +371,49 @@ public struct SwiftlyHTTPClient {
363371
limit: Int? = nil,
364372
filter: ((ToolchainVersion.Snapshot) -> Bool)? = nil
365373
) async throws -> [ToolchainVersion.Snapshot] {
366-
let arch = a ?? cpuArch.value2
374+
let platformId: Components.Schemas.KnownPlatformIdentifier = switch platform.name {
375+
// case PlatformDefinition.ubuntu2404.name:
376+
// .ubuntu2404
377+
// case PlatformDefinition.debian12.name:
378+
// .debian12
379+
// case PlatformDefinition.fedora39.name:
380+
// .fedora39
381+
case PlatformDefinition.ubuntu2204.name:
382+
.ubuntu2204
383+
case PlatformDefinition.ubuntu2004.name:
384+
.ubuntu2004
385+
case PlatformDefinition.rhel9.name:
386+
.ubi9
387+
case PlatformDefinition.amazonlinux2.name:
388+
.amazonlinux2
389+
case PlatformDefinition.macOS.name:
390+
.macos
391+
default:
392+
throw SwiftlyError(message: "No snapshot toolchains available for platform \(platform.name)")
393+
}
367394

368-
let platformName = if platform.name == PlatformDefinition.macOS.name {
369-
"macos"
370-
} else {
371-
platform.name
395+
let sourceBranch: Components.Schemas.KnownSourceBranch = switch branch {
396+
case .main:
397+
.main
398+
case .release(major: 6, minor: 0):
399+
._6_0
400+
default:
401+
throw SwiftlyError(message: "Unknown snapshot branch: \(branch)")
372402
}
373403

374-
let url = "https://www.swift.org/api/v1/install/dev/\(branch.name)/\(platformName).json"
404+
let devToolchains = try await SwiftlyCore.httpRequestExecutor.getSnapshotToolchains(branch: sourceBranch, platform: platformId)
375405

376-
// For a particular branch and platform the snapshots are listed underneath their architecture
377-
let swiftOrgSnapshotArchs: SwiftOrgSnapshotList
378-
do {
379-
swiftOrgSnapshotArchs = try await self.getFromJSON(url: url, type: SwiftOrgSnapshotList.self)
380-
} catch is JSONNotFoundError {
381-
throw SnapshotBranchNotFoundError(branch: branch)
382-
} catch {
383-
throw error
384-
}
406+
let arch = a ?? cpuArch.value2
385407

386408
// These are the available snapshots for the branch, platform, and architecture
387409
let swiftOrgSnapshots = if platform.name == PlatformDefinition.macOS.name {
388-
swiftOrgSnapshotArchs.universal ?? [SwiftOrgSnapshot]()
410+
devToolchains.universal ?? [Components.Schemas.DevToolchainForArch]()
389411
} else if arch == "aarch64" {
390-
swiftOrgSnapshotArchs.aarch64 ?? [SwiftOrgSnapshot]()
412+
devToolchains.aarch64 ?? [Components.Schemas.DevToolchainForArch]()
391413
} else if arch == "x86_64" {
392-
swiftOrgSnapshotArchs.x86_64 ?? [SwiftOrgSnapshot]()
414+
devToolchains.x8664 ?? [Components.Schemas.DevToolchainForArch]()
393415
} else {
394-
[SwiftOrgSnapshot]()
416+
[Components.Schemas.DevToolchainForArch]()
395417
}
396418

397419
// Convert these into toolchain snapshot versions that match the filter

Tests/SwiftlyTests/SwiftlyTests.swift

Lines changed: 53 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,10 +39,10 @@ public class SwiftlyTests: XCTestCase {
3939
public static let oldStable = ToolchainVersion(major: 5, minor: 6, patch: 0)
4040
public static let oldStableNewPatch = ToolchainVersion(major: 5, minor: 6, patch: 3)
4141
public static let newStable = ToolchainVersion(major: 5, minor: 7, patch: 0)
42-
public static let oldMainSnapshot = ToolchainVersion(snapshotBranch: .main, date: "2022-09-10")
43-
public static let newMainSnapshot = ToolchainVersion(snapshotBranch: .main, date: "2022-10-22")
44-
public static let oldReleaseSnapshot = ToolchainVersion(snapshotBranch: .release(major: 5, minor: 7), date: "2022-08-27")
45-
public static let newReleaseSnapshot = ToolchainVersion(snapshotBranch: .release(major: 5, minor: 7), date: "2022-08-30")
42+
public static let oldMainSnapshot = ToolchainVersion(snapshotBranch: .main, date: "2025-03-10")
43+
public static let newMainSnapshot = ToolchainVersion(snapshotBranch: .main, date: "2025-03-14")
44+
public static let oldReleaseSnapshot = ToolchainVersion(snapshotBranch: .release(major: 6, minor: 0), date: "2025-02-09")
45+
public static let newReleaseSnapshot = ToolchainVersion(snapshotBranch: .release(major: 6, minor: 0), date: "2025-02-11")
4646

4747
static let allToolchains: Set<ToolchainVersion> = [
4848
oldStable,
@@ -471,6 +471,10 @@ private struct MockHTTPRequestExecutor: HTTPRequestExecutor {
471471
public func getReleaseToolchains() async throws -> [Components.Schemas.Release] {
472472
throw SwiftlyTestError(message: "Mocking of fetching the release toolchains is not implemented in MockHTTPRequestExecutor.")
473473
}
474+
475+
public func getSnapshotToolchains(branch _: Components.Schemas.KnownSourceBranch, platform _: Components.Schemas.KnownPlatformIdentifier) async throws -> Components.Schemas.DevToolchains {
476+
throw SwiftlyTestError(message: "Mocking of fetching the snapshot toolchains is not implemented in MockHTTPRequestExecutor.")
477+
}
474478
}
475479

476480
/// An `HTTPRequestExecutor` which will return a mocked response to any toolchain download requests.
@@ -491,6 +495,8 @@ public class MockToolchainDownloader: HTTPRequestExecutor {
491495

492496
private let releaseToolchains: [ToolchainVersion.StableRelease]
493497

498+
private let snapshotToolchains: [ToolchainVersion.Snapshot]
499+
494500
public init(
495501
executables: [String]? = nil,
496502
latestSwiftlyVersion: SwiftlyVersion = SwiftlyCore.version,
@@ -505,6 +511,12 @@ public class MockToolchainDownloader: HTTPRequestExecutor {
505511
ToolchainVersion.StableRelease(major: 6, minor: 0, patch: 1), // Some tests try to update from 6.0.0
506512
ToolchainVersion.StableRelease(major: 6, minor: 0, patch: 2), // Some tests try to update from 6.0.1
507513
],
514+
snapshotToolchains: [ToolchainVersion.Snapshot] = [
515+
SwiftlyTests.oldMainSnapshot.asSnapshot!,
516+
SwiftlyTests.newMainSnapshot.asSnapshot!,
517+
SwiftlyTests.oldReleaseSnapshot.asSnapshot!,
518+
SwiftlyTests.newReleaseSnapshot.asSnapshot!,
519+
],
508520
delegate: HTTPRequestExecutor
509521
) {
510522
self.executables = executables ?? ["swift"]
@@ -514,6 +526,7 @@ public class MockToolchainDownloader: HTTPRequestExecutor {
514526
self.delegate = delegate
515527
self.latestSwiftlyVersion = latestSwiftlyVersion
516528
self.releaseToolchains = releaseToolchains
529+
self.snapshotToolchains = snapshotToolchains
517530
}
518531

519532
public func getCurrentSwiftlyRelease() async throws -> Components.Schemas.SwiftlyRelease {
@@ -572,6 +585,42 @@ public class MockToolchainDownloader: HTTPRequestExecutor {
572585
}
573586
}
574587

588+
public func getSnapshotToolchains(branch: Components.Schemas.KnownSourceBranch, platform _: Components.Schemas.KnownPlatformIdentifier) async throws -> Components.Schemas.DevToolchains {
589+
let currentPlatform = try await Swiftly.currentPlatform.detectPlatform(disableConfirmation: true, platform: nil)
590+
591+
let releasesForBranch = self.snapshotToolchains.filter { snapshotVersion in
592+
switch snapshotVersion.branch {
593+
case .main:
594+
branch == .main
595+
case let .release(major, minor):
596+
major == 6 && minor == 0 && branch == ._6_0
597+
}
598+
}
599+
600+
let devToolchainsForArch = releasesForBranch.map { branchSnapshot in
601+
Components.Schemas.DevToolchainForArch(
602+
name: Components.Schemas.DevToolchainKind?.none,
603+
date: "",
604+
dir: branch == .main ?
605+
"swift-DEVELOPMENT-SNAPSHOT-\(branchSnapshot.date)" :
606+
"swift-6.0-DEVELOPMENT-SNAPSHOT-\(branchSnapshot.date)",
607+
download: "",
608+
downloadSignature: nil,
609+
debugInfo: nil
610+
)
611+
}
612+
613+
if currentPlatform == PlatformDefinition.macOS {
614+
return Components.Schemas.DevToolchains(universal: devToolchainsForArch)
615+
} else if cpuArch == Components.Schemas.Architecture.x8664 {
616+
return Components.Schemas.DevToolchains(x8664: devToolchainsForArch)
617+
} else if cpuArch == Components.Schemas.Architecture.aarch64 {
618+
return Components.Schemas.DevToolchains(aarch64: devToolchainsForArch)
619+
} else {
620+
return Components.Schemas.DevToolchains()
621+
}
622+
}
623+
575624
public func execute(_ request: HTTPClientRequest, timeout: TimeAmount) async throws -> HTTPClientResponse {
576625
guard let url = URL(string: request.url) else {
577626
throw SwiftlyTestError(message: "invalid request URL: \(request.url)")

Tests/SwiftlyTests/UpdateTests.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,7 @@ final class UpdateTests: SwiftlyTests {
177177
for branch in branches {
178178
try await self.withTestHome {
179179
try await self.withMockedToolchain {
180-
let date = "2024-06-18"
180+
let date = branch == .main ? SwiftlyTests.oldMainSnapshot.asSnapshot!.date : SwiftlyTests.oldReleaseSnapshot.asSnapshot!.date
181181
try await self.installMockedToolchain(selector: .snapshot(branch: branch, date: date))
182182

183183
var update = try self.parseCommand(

0 commit comments

Comments
 (0)