Skip to content

Commit 4e7ca7c

Browse files
committed
Merge branch 'main' of github.com:cmcgee1024/swiftly into add_new_linux_platforms
2 parents be65db1 + 94ec29c commit 4e7ca7c

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+153
-16058
lines changed

Documentation/SwiftlyDocs.docc/swiftly-cli-reference.md

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ swiftly [--version] [--help]
2323
Install a new toolchain.
2424

2525
```
26-
swiftly install <version> [--use] [--token=<token>] [--verify] [--post-install-file=<post-install-file>] [--version] [--help]
26+
swiftly install <version> [--use] [--verify] [--post-install-file=<post-install-file>] [--version] [--help]
2727
```
2828

2929
**version:**
@@ -59,14 +59,6 @@ Likewise, the latest snapshot associated with a given development branch can be
5959
*Mark the newly installed toolchain as in-use.*
6060

6161

62-
**--token=\<token\>:**
63-
64-
*A GitHub authentication token to use for any GitHub API requests.*
65-
66-
This is useful to avoid GitHub's low rate limits. If an installation
67-
fails with an "unauthorized" status code, it likely means the rate limit has been hit.
68-
69-
7062
**--verify:**
7163

7264
*Verify the toolchain's PGP signature before proceeding with installation.*
@@ -120,9 +112,9 @@ Likewise, the available toolchains associated with a given minor version can be
120112
The installed snapshots for a given devlopment branch can be listed by specifying the branch as the selector:
121113

122114
$ swiftly list-available main-snapshot
123-
$ swiftly list-available 6.0-snapshot
115+
$ swiftly list-available x.y-snapshot
124116

125-
Note that listing available snapshots before 6.0 is unsupported.
117+
Note that listing available snapshots before the latest release (major and minor number) is unsupported.
126118

127119

128120
**--version:**

Package.swift

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,6 @@
22

33
import PackageDescription
44

5-
let ghApiCacheResources = (1...16).map { Resource.embedInCode("gh-api-cache/swift-tags-page\($0).json") }
6-
let ghApiCacheExcludedResources = (17...27).map { "gh-api-cache/swift-tags-page\($0).json" }
7-
85
let package = Package(
96
name: "swiftly",
107
platforms: [
@@ -100,8 +97,7 @@ let package = Package(
10097
.testTarget(
10198
name: "SwiftlyTests",
10299
dependencies: ["Swiftly"],
103-
exclude: ghApiCacheExcludedResources,
104-
resources: ghApiCacheResources + [
100+
resources: [
105101
.embedInCode("mock-signing-key-private.pgp"),
106102
]
107103
),

README.md

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -272,13 +272,6 @@ swift-5.7-DEVELOPMENT-SNAPSHOT-2022-08-30-a
272272

273273
The canonical name format was chosen to reduce the keystrokes needed to refer to a snapshot toolchain, but the longer form is also useful when copy/pasting a toolchain name provided from somewhere else.
274274

275-
### Specifying a GitHub access token
276-
277-
swiftly currently uses the GitHub API to look up the available Swift toolchains. To avoid running up against rate limits, you can provide a [GitHub access token](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/creating-a-personal-access-token) via the `--token` option (the token doesn't need any permissions):
278-
279-
```
280-
$ swiftly install latest --token <GitHub authentication token>
281-
```
282275
## Contributing
283276
Welcome to the Swift community!
284277

@@ -298,7 +291,7 @@ Swift.org currently provides experimental [`.rpm` and `.deb`](https://forums.swi
298291

299292
swiftenv is an existing Swift version manager which already has much of the functionality that swiftly will eventually have. It's an awesome tool, and if it's part of your workflow then we encourage you to keep using it! That said, swiftly is/will be different a few ways:
300293

301-
- swiftly is being built as a community driven effort led by the Swift server workgroup, and through this collaboration, swiftly will eventually become an official installation tool for Swift toolchains. As first step towards that, swiftly will help inform the creation of API endpoints maintained by the Swift project that it will use to retrieve information about what toolchains are available to install and to verify their expected signatures. swiftenv currently uses a third party API layer for this. Using an official API reduces the avenues for security vulnerabilities and also reduces the risk of downtime affecting Swift installations. Note that this is planned for the future--swiftly currently uses the GitHub API for this purpose.
294+
- swiftly is being built as a community driven effort led by the Swift server workgroup, and through this collaboration, swiftly will eventually become an official installation tool for Swift toolchains. As first step towards that, swiftly will help inform the creation of API endpoints maintained by the Swift project that it will use to retrieve information about what toolchains are available to install and to verify their expected signatures. swiftenv currently uses a third party API layer for this. Using an official API reduces the avenues for security vulnerabilities and also reduces the risk of downtime affecting Swift installations.
302295

303296
- swiftly will be written in Swift, which we think is important for maintainability and encouraging community contributions.
304297

RELEASING.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ Swift has a tool for producing final product packages, suitable for distribution
1313
5. Create a tag on that commit with the format "x.y.z". Do not omit "z", even if its value is 0.
1414

1515
6. Build the executables for the release by running `swift run build-swiftly-release <version>` from the root of the swiftly repository
16-
* Build on a Apple silicon macOS machine to produce a universal package for x86_64 and arm64
16+
* Build on an Apple macOS machine to produce a universal package for x86_64 and arm64 (add the --cert option to provide a signing certificate file for the .pkg)
1717
* Build on an Amazon Linux 2 image for x86_64
1818
* Build on an Amazon Linux 2 image for arm64
1919

Sources/LinuxPlatform/Linux.swift

Lines changed: 29 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -247,23 +247,23 @@ public struct Linux: Platform {
247247

248248
let manager: String? = switch platformName {
249249
case "ubuntu1804":
250-
"apt"
250+
"apt-get"
251251
case "ubuntu2004":
252-
"apt"
252+
"apt-get"
253253
case "ubuntu2204":
254-
"apt"
254+
"apt-get"
255255
case "ubuntu2310":
256-
"apt"
256+
"apt-get"
257257
case "ubuntu2404":
258-
"apt"
258+
"apt-get"
259259
case "amazonlinux2":
260260
"yum"
261261
case "ubi9":
262262
"yum"
263263
case "fedora39":
264264
"yum"
265265
case "debian12":
266-
"apt"
266+
"apt-get"
267267
default:
268268
nil
269269
}
@@ -307,21 +307,38 @@ public struct Linux: Platform {
307307
return nil
308308
}
309309

310-
let missingPackages = packages.filter { !self.isSystemPackageInstalled(manager, $0) }.joined(separator: " ")
310+
var missingPackages: [String] = []
311+
312+
for pkg in packages {
313+
if case let pkgInstalled = await self.isSystemPackageInstalled(manager, pkg), !pkgInstalled {
314+
missingPackages.append(pkg)
315+
}
316+
}
311317

312318
guard !missingPackages.isEmpty else {
313319
return nil
314320
}
315321

316-
return "\(manager) -y install \(missingPackages)"
322+
return "\(manager) -y install \(missingPackages.joined(separator: " "))"
317323
}
318324

319-
public func isSystemPackageInstalled(_ manager: String?, _ package: String) -> Bool {
325+
public func isSystemPackageInstalled(_ manager: String?, _ package: String) async -> Bool {
320326
do {
321327
switch manager {
322-
case "apt":
323-
try self.runProgram("dpkg", "-l", package, quiet: true)
324-
return true
328+
case "apt-get":
329+
if let pkgList = try await self.runProgramOutput("dpkg", "-l", package) {
330+
// The package might be listed but not in an installed non-error state.
331+
//
332+
// Look for something like this:
333+
//
334+
// Desired=Unknown/Install/Remove/Purge/Hold
335+
// | Status=Not/Inst/Conf-files/Unpacked/halF-conf/Half-inst/trig-aWait/Trig-pend
336+
// |/ Err?=(none)/Reinst-required (Status,Err: uppercase=bad)
337+
// ||/
338+
// ii pkgfoo 1.0.0ubuntu12 My description goes here....
339+
return pkgList.contains("\nii ")
340+
}
341+
return false
325342
case "yum":
326343
try self.runProgram("yum", "list", "installed", package, quiet: true)
327344
return true

Sources/Swiftly/Install.swift

Lines changed: 17 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -46,15 +46,6 @@ struct Install: SwiftlyCommand {
4646
@Flag(name: .shortAndLong, help: "Mark the newly installed toolchain as in-use.")
4747
var use: Bool = false
4848

49-
@Option(help: ArgumentHelp(
50-
"A GitHub authentication token to use for any GitHub API requests.",
51-
discussion: """
52-
This is useful to avoid GitHub's low rate limits. If an installation
53-
fails with an \"unauthorized\" status code, it likely means the rate limit has been hit.
54-
"""
55-
))
56-
var token: String?
57-
5849
@Flag(inversion: .prefixedNo, help: "Verify the toolchain's PGP signature before proceeding with installation.")
5950
var verify = true
6051

@@ -68,15 +59,14 @@ struct Install: SwiftlyCommand {
6859
var postInstallFile: String?
6960

7061
private enum CodingKeys: String, CodingKey {
71-
case version, token, use, verify, postInstallFile
62+
case version, use, verify, postInstallFile
7263
}
7364

7465
mutating func run() async throws {
7566
try validateSwiftly()
7667

7768
let selector = try ToolchainSelector(parsing: self.version)
7869
var config = try Config.load()
79-
SwiftlyCore.httpClient.githubToken = self.token
8070
let toolchainVersion = try await self.resolve(config: config, selector: selector)
8171
let postInstallScript = try await Self.execute(
8272
version: toolchainVersion,
@@ -224,8 +214,7 @@ struct Install: SwiftlyCommand {
224214
return postInstallScript
225215
}
226216

227-
/// Utilize the GitHub API along with the provided selector to select a toolchain for install.
228-
/// TODO: update this to use an official swift.org API
217+
/// Utilize the swift.org API along with the provided selector to select a toolchain for install.
229218
func resolve(config: Config, selector: ToolchainSelector) async throws -> ToolchainVersion {
230219
switch selector {
231220
case .latest:
@@ -266,17 +255,27 @@ struct Install: SwiftlyCommand {
266255
}
267256

268257
SwiftlyCore.print("Fetching the latest \(branch) branch snapshot...")
258+
269259
// If a date was not provided, perform a lookup to find the most recent snapshot
270260
// for the given branch.
271-
let snapshot = try await SwiftlyCore.httpClient.getSnapshotToolchains(platform: config.platform, branch: branch, limit: 1) { snapshot in
272-
snapshot.branch == branch
273-
}.first
261+
let snapshots: [ToolchainVersion.Snapshot]
262+
do {
263+
snapshots = try await SwiftlyCore.httpClient.getSnapshotToolchains(platform: config.platform, branch: branch, limit: 1) { snapshot in
264+
snapshot.branch == branch
265+
}
266+
} catch let branchNotFoundErr as SwiftlyHTTPClient.SnapshotBranchNotFoundError {
267+
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.")
268+
} catch {
269+
throw error
270+
}
271+
272+
let firstSnapshot = snapshots.first
274273

275-
guard let snapshot else {
274+
guard let firstSnapshot else {
276275
throw Error(message: "No snapshot toolchain found for branch \(branch)")
277276
}
278277

279-
return .snapshot(snapshot)
278+
return .snapshot(firstSnapshot)
280279
}
281280
}
282281
}

Sources/Swiftly/ListAvailable.swift

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,9 @@ struct ListAvailable: SwiftlyCommand {
2828
The installed snapshots for a given devlopment branch can be listed by specifying the branch as the selector:
2929
3030
$ swiftly list-available main-snapshot
31-
$ swiftly list-available 6.0-snapshot
31+
$ swiftly list-available x.y-snapshot
3232
33-
Note that listing available snapshots before 6.0 is unsupported.
33+
Note that listing available snapshots before the latest release (major and minor number) is unsupported.
3434
"""
3535
))
3636
var toolchainSelector: String?
@@ -51,11 +51,13 @@ struct ListAvailable: SwiftlyCommand {
5151

5252
switch selector {
5353
case let .snapshot(branch, _):
54-
if case let .release(major, _) = branch, major < 6 {
55-
throw Error(message: "Listing available snapshots previous to 6.0 is not supported.")
54+
do {
55+
tc = try await SwiftlyCore.httpClient.getSnapshotToolchains(platform: config.platform, branch: branch).map { ToolchainVersion.snapshot($0) }
56+
} catch let branchNotFoundError as SwiftlyHTTPClient.SnapshotBranchNotFoundError {
57+
throw Error(message: "The snapshot branch \(branchNotFoundError.branch) was not found on swift.org. Note that snapshot toolchains are only available for the current `main` release and the previous x.y (major.minor) release.")
58+
} catch {
59+
throw error
5660
}
57-
58-
tc = try await SwiftlyCore.httpClient.getSnapshotToolchains(platform: config.platform, branch: branch).map { ToolchainVersion.snapshot($0) }
5961
case .stable, .latest:
6062
tc = try await SwiftlyCore.httpClient.getReleaseToolchains(platform: config.platform).map { ToolchainVersion.stable($0) }
6163
default:

Sources/Swiftly/Update.swift

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,7 @@ struct Update: SwiftlyCommand {
180180
}
181181

182182
/// Tries to find a toolchain version that meets the provided parameters, if one exists.
183-
/// This does not download the toolchain, but it does query the GitHub API to find the suitable toolchain.
183+
/// This does not download the toolchain, but it does query the swift.org API to find the suitable toolchain.
184184
private func lookupNewToolchain(_ config: Config, _ bounds: UpdateParameters) async throws -> ToolchainVersion? {
185185
switch bounds {
186186
case let .stable(old, range):
@@ -195,9 +195,18 @@ struct Update: SwiftlyCommand {
195195
}
196196
}.first.map(ToolchainVersion.stable)
197197
case let .snapshot(old):
198-
return try await SwiftlyCore.httpClient.getSnapshotToolchains(platform: config.platform, branch: old.branch, limit: 1) { snapshot in
199-
snapshot.branch == old.branch && snapshot.date > old.date
200-
}.first.map(ToolchainVersion.snapshot)
198+
let newerSnapshotToolchains: [ToolchainVersion.Snapshot]
199+
do {
200+
newerSnapshotToolchains = try await SwiftlyCore.httpClient.getSnapshotToolchains(platform: config.platform, branch: old.branch, limit: 1) { snapshot in
201+
snapshot.branch == old.branch && snapshot.date > old.date
202+
}
203+
} catch let branchNotFoundErr as SwiftlyHTTPClient.SnapshotBranchNotFoundError {
204+
throw Error(message: "Snapshot branch \(branchNotFoundErr.branch) cannot be updated. One possible reason for this is that there has been a new release published to swift.org and this snapshot is for an older release. Snapshots are only available for the newest release and the main branch. You can install a fresh snapshot toolchain from the either the latest release x.y (major.minor) with `swiftly install x.y-snapshot` or from the main branch with `swiftly install main-snapshot`.")
205+
} catch {
206+
throw error
207+
}
208+
209+
return newerSnapshotToolchains.first.map(ToolchainVersion.snapshot)
201210
}
202211
}
203212
}

0 commit comments

Comments
 (0)