Skip to content

Use swift.org API for getting releases and some snapshot toolchains #153

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 9 commits into from
Aug 23, 2024

Conversation

cmcgee1024
Copy link
Member

@cmcgee1024 cmcgee1024 commented Aug 8, 2024

See issue #149

Using the new swift.org API is more accurate than the GitHub API, also it isn't subject to the same rate limits. Use the swift.org API instead of the GitHub API for fetching metadata about the released swift toolchains, and the newer swift branches for the snapshots.

However, there is currently no metadata for snapshots on older branches than 6.0 or main. In this case use the GitHub API to get the metadata, but only for swiftly install and not the swiftly list-available commands, since the latter is very expensive when using the GitHub API.

@cmcgee1024
Copy link
Member Author

@swift-ci test macOS

@cmcgee1024 cmcgee1024 marked this pull request as ready for review August 9, 2024 11:26
@cmcgee1024 cmcgee1024 changed the title DRAFT: Use swift.org API for getting releases and snapshot toolchains Use swift.org API for getting releases and some snapshot toolchains Aug 9, 2024
@cmcgee1024
Copy link
Member Author

The macOS CI passes the tests, but there's the persistent environmental problem that reports an overall failure.

Test Suite 'All tests' passed at 2024-08-09 11:22:46.405.
	 Executed 67 tests, with 0 failures (0 unexpected) in 72.886 (72.895) seconds

Copy link
Contributor

@adam-fowler adam-fowler left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In general looks good, I've a couple of questions and minor edit requests


Note that listing available snapshots is currently unsupported. It will be introduced in a future release.
Note that listing available snapshots before 6.0 is unsupported.
Copy link
Contributor

@adam-fowler adam-fowler Aug 10, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I assumed this is because the data isn't available at the moment. Could this change?

print("----------------------------")
for toolchain in toolchains where toolchain.isStableRelease() {
for toolchain in toolchains {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good call don't need to separate these two lists

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The output of swiftly list separates them still, so I think it would be a good idea to be consistent with that. Regardless of that, they were originally separated in part because snapshots are released on a (nearly?) daily basis, so they would always dominate the output here, and I'd imagine that most users are looking for available releases anyways. So I'd support continuing to list these separately.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This else only gets invoked in the absence of a selector, which defaults to all of the releases, since every snapshot build for every branch would be overwhelming and probably not very useful.

I can retain the "release" toolchains label to make it clear that these are only releases, but the where clause shouldn't be necessary here.

@@ -22,6 +22,100 @@ private func makeRequest(url: String) -> HTTPClientRequest {
return request
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it worthwhile breaking out the swift.org data and parsing into a separate file?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm on the fence about this.

It feels like the swift.org metadata is a foundational use of the swiftly http client, so why not just put the code there? On the other hand, because of the GitHub fallback there's some asymmetry now because it's in another source file. But, there's also a feeling that the fallback may go away someday, so that could be why it is separate.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As I see it there is the client and the usage of the client which are two different things

@@ -88,21 +207,91 @@ public struct SwiftlyHTTPClient {
return release
}

return try await self.mapGitHubTags(limit: limit, filterMap: filterMap) { page in
try await self.getReleases(page: page)
guard !swiftOrgFiltered.isEmpty else {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this really necessary?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed, this is not needed. I'll remove it.

print("----------------------------")
for toolchain in toolchains where toolchain.isStableRelease() {
for toolchain in toolchains {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The output of swiftly list separates them still, so I think it would be a good idea to be consistent with that. Regardless of that, they were originally separated in part because snapshots are released on a (nearly?) daily basis, so they would always dominate the output here, and I'd imagine that most users are looking for available releases anyways. So I'd support continuing to list these separately.

return if let limit = limit {
Array(finalSnapshotToolchains[0..<limit])
} else {
finalSnapshotToolchains
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have several variables for the snapshots, but I think we only really need swiftOrgFiltered. Can we drop finalSnapshotToolchains and snapshotToolchains?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm looking into making this simpler and easier to follow.

@cmcgee1024
Copy link
Member Author

@swift-ci test macOS

@cmcgee1024
Copy link
Member Author

The macOS CI passed the xctests despite reporting an overall failure:

Test Suite 'All tests' passed at 2024-08-20 16:11:50.665.
	 Executed 67 tests, with 0 failures (0 unexpected) in 73.780 (73.789) seconds

@cmcgee1024
Copy link
Member Author

@patrickfreed @adam-fowler how do these latest changes look? Any remaining issues?

Copy link
Contributor

@patrickfreed patrickfreed left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall looks good, super excited to see swiftly using official APIs now. I have a bunch of smaller, mostly style suggestions. Feel free to merge after addressing without waiting for another round of feedback from me.

*A filter to use when listing toolchains.*


The toolchain selector determines which toolchains to list. If no selector is provided, all available toolchains will be listed:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should note here that only release toolchains will be listed.

.map(ToolchainVersion.stable)
.filter { selector?.matches(toolchain: $0) ?? true }
tc = try await SwiftlyCore.httpClient.getSnapshotToolchains(platform: config.platform, branch: branch).map { ToolchainVersion.snapshot($0) }
default:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rather than using default here, can we explicitly check for .release? It reads a bit clearer that way.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, I've changed this to add explicit stable/latest case, and a default to catch no selector provided.

Comment on lines +172 to +179
let arch = if a == nil {
#if arch(x86_64)
"x86_64"
#elseif arch(arm64)
"aarch64"
#else
#error("Unsupported processor architecture")
#endif
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we do this in a few places, can we extract this to a method/computed variable on Platform? I think it would also be good to define an enum for it so we don't have to rely on magic strings here and elsewhere.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This pattern is isolated to two places at the moment, mostly isolated to path/filename calculation and internal swift org metadata. It might be possible to isolate all of it in the HTTPClient, or an HTTPClient+SwiftOrg.

There are some problems with pulling this out into a global enum at the Platform level. In macOS, the arch isn't used at all because of the universal binaries/packages. Also, there is some disagreement between the naming of the 64-bit arm architecture. In some places. it's "arm64" while others is "aarch64." It doesn't feel like an enum could be universally defined across OSes and cases without being a leaky abstraction.

I feel that in Swift it's quite common to put these kinds of macros (os/arch) at the place where they are needed as long as it isn't excessive and it can maintain locality of change when/if the logic changes. For now, it's only these two places: HTTPClient, and Install.

PlatformDefinition.macOS,
PlatformDefinition.ubuntu2204,
PlatformDefinition.ubuntu2004,
// PlatformDefinition.ubuntu1804,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why commented out?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ubuntu 18.04 doesn't have toolchains available for the releases that we are testing here. I'll update the comment to explain.

Comment on lines 265 to 270
let branchLabel = switch branch {
case .main:
"main"
case let .release(major, minor):
"\(major).\(minor)"
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

branch.name gives us this.

let swiftOrgReleases: [SwiftOrgRelease] = try await self.getFromJSON(url: url, type: [SwiftOrgRelease].self)

var swiftOrgFiltered: [ToolchainVersion.StableRelease] = swiftOrgReleases.compactMap { swiftOrgRelease in
if platform.name != "xcode" {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"xcode" -> PlatformDefinition.xcode.name

}
}

guard let version = try? ToolchainVersion(parsing: swiftOrgRelease.stableName),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if parsing fails, I think we'd want to let the user know about it in some way, either just by throwing here (which I think is fine, since we should never fail parsing swift.org results) or logging.

"\(major).\(minor)"
}

let platformName = if platform.name == "xcode" {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"xcode" -> PlatformDefinition.xcode.name, also below.


/// platformName is a mapping from the 'name' field of the swift.org platform object
/// to swiftly's PlatformDefinition name.
var platformName: String {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To better align with this var's usage/documentation, it might be better to just return the matching PlatformDefinition or nil if it isn't supported. That way we can rely on the PlatformDefinition constants as the single source of supported platform names, and we also don't need to define constants for platforms we don't support. If we go that direction, renaming this something like asPlatformDefinition or something might make sense.

If we don't change this in that way, then I think at least renaming it to canonicalPlatformName or swiftlyPlatformName communicates the difference between the other name field a bit better. We'd also want to use the PlatformDefinition name constants here as well.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, this is a really nice pattern here. We can return the constants defined in PlatformDefinition for platforms that swiftly supports, and manually constructed PlatformDefinitions for the ones that it doesn't, which can provide enough metadata to represent what the swift.org API is producing. For yet unknown (to swiftly) swift.org platforms, then it's just nil until this code adds a representation for it.

}

let platformName = if platform.name == "xcode" {
"macos"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the inconsistency between these swift.org APIs and swift.org's download URLs here is a bit odd. Is there any chance the API would be updated to use "xcode" like the downloads do so we can avoid needing to adjust for it here?

This question also applies for the non-macOS platform names. It'd be nice if e.g. ubuntu1804 were used rather than the pretty name.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@shahmishal here is some feedback regarding the swift.org API. There are some inconsistencies between the new API and the toolchain download URL's / filenames. We're working around these for now in swiftly, but perhaps this can be made a little more ergonomic for other consumers of the API.

Use PlatformDefinition constants for cases of swiftly supported platforms

Construct PlatformDefinitions for known swift.org platforms that are not supported by swiftly

Use branch.name for snapshot url construction

Update command-line reference documentation of list-available for the case of no selector
@cmcgee1024
Copy link
Member Author

@swift-ci test macOS

@cmcgee1024 cmcgee1024 merged commit b0dc1b3 into swiftlang:main Aug 23, 2024
5 of 6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants