Skip to content

Commit ac4b473

Browse files
committed
Refactor config loading, use in use toolchain when linking
1 parent 11b847c commit ac4b473

File tree

13 files changed

+81
-178
lines changed

13 files changed

+81
-178
lines changed

Sources/Swiftly/Install.swift

Lines changed: 42 additions & 130 deletions
Original file line numberDiff line numberDiff line change
@@ -82,9 +82,10 @@ struct Install: SwiftlyCommand {
8282
}
8383

8484
mutating func run(_ ctx: SwiftlyCoreContext) async throws {
85-
try await validateSwiftly(ctx)
85+
try await validatedConfig(ctx)
8686

8787
var config = try await Config.load(ctx)
88+
let toolchainVersion = try await Self.determineToolchainVersion(ctx, version: self.version, config: &config)
8889

8990
let (postInstallScript, pathChanged) = try await Self.execute(
9091
ctx,
@@ -106,12 +107,29 @@ struct Install: SwiftlyCommand {
106107

107108
// Fish doesn't cache its path, so this instruction is not necessary.
108109
if pathChanged && !shell.hasSuffix("fish") {
109-
await ctx.print(Messages.refreshShell)
110+
await ctx.print(
111+
"""
112+
NOTE: Swiftly has updated some elements in your path and your shell may not yet be
113+
aware of the changes. You can update your shell's environment by running
114+
115+
hash -r
116+
117+
or restarting your shell.
118+
119+
""")
110120
}
111121

112122
if let postInstallScript {
113123
guard let postInstallFile = self.postInstallFile else {
114-
throw SwiftlyError(message: Messages.postInstall(postInstallScript))
124+
throw SwiftlyError(
125+
message: """
126+
127+
There are some dependencies that should be installed before using this toolchain.
128+
You can run the following script as the system administrator (e.g. root) to prepare
129+
your system:
130+
131+
\(postInstallScript)
132+
""")
115133
}
116134

117135
try Data(postInstallScript.utf8).write(
@@ -129,21 +147,23 @@ struct Install: SwiftlyCommand {
129147
var pathChanged = false
130148

131149
// Create proxies if we have a location where we can point them
132-
if let proxyTo = try? Swiftly.currentPlatform.findSwiftlyBin(ctx) {
150+
if let proxyTo = try? await Swiftly.currentPlatform.findSwiftlyBin(ctx) {
133151
// Ensure swiftly doesn't overwrite any existing executables without getting confirmation first.
134152
let swiftlyBinDir = Swiftly.currentPlatform.swiftlyBinDir(ctx)
135153
let swiftlyBinDirContents =
136-
(try? FileManager.default.contentsOfDirectory(atPath: swiftlyBinDir.path)) ?? [String]()
154+
(try? await fs.ls(atPath: swiftlyBinDir)) ?? [String]()
137155
let toolchainBinDir = Swiftly.currentPlatform.findToolchainBinDir(ctx, version)
138-
let toolchainBinDirContents = try FileManager.default.contentsOfDirectory(
139-
atPath: toolchainBinDir.path)
156+
let toolchainBinDirContents = try await fs.ls(atPath: toolchainBinDir)
140157

141-
let existingProxies = swiftlyBinDirContents.filter { bin in
158+
var existingProxies: [String] = []
159+
160+
for bin in swiftlyBinDirContents {
142161
do {
143-
let linkTarget = try FileManager.default.destinationOfSymbolicLink(
144-
atPath: swiftlyBinDir.appendingPathComponent(bin).path)
145-
return linkTarget == proxyTo
146-
} catch { return false }
162+
let linkTarget = try await fs.readlink(atPath: swiftlyBinDir / bin)
163+
if linkTarget == proxyTo {
164+
existingProxies.append(bin)
165+
}
166+
} catch { continue }
147167
}
148168

149169
let overwrite = Set(toolchainBinDirContents).subtracting(existingProxies).intersection(
@@ -152,7 +172,7 @@ struct Install: SwiftlyCommand {
152172
await ctx.print("The following existing executables will be overwritten:")
153173

154174
for executable in overwrite {
155-
await ctx.print(" \(swiftlyBinDir.appendingPathComponent(executable).path)")
175+
await ctx.print(" \(swiftlyBinDir / executable)")
156176
}
157177

158178
guard await ctx.promptForConfirmation(defaultBehavior: false) else {
@@ -168,16 +188,13 @@ struct Install: SwiftlyCommand {
168188
overwrite)
169189

170190
for p in proxiesToCreate {
171-
let proxy = Swiftly.currentPlatform.swiftlyBinDir(ctx).appendingPathComponent(p)
191+
let proxy = Swiftly.currentPlatform.swiftlyBinDir(ctx) / p
172192

173-
if proxy.fileExists() {
174-
try FileManager.default.removeItem(at: proxy)
193+
if try await fs.exists(atPath: proxy) {
194+
try await fs.remove(atPath: proxy)
175195
}
176196

177-
try FileManager.default.createSymbolicLink(
178-
atPath: proxy.path,
179-
withDestinationPath: proxyTo
180-
)
197+
try await fs.symlink(atPath: proxy, linkPath: proxyTo)
181198

182199
pathChanged = true
183200
}
@@ -327,120 +344,15 @@ struct Install: SwiftlyCommand {
327344

328345
try await Swiftly.currentPlatform.install(ctx, from: tmpFile, version: version, verbose: verbose)
329346

330-
var pathChanged = false
331-
332-
// Create proxies if we have a location where we can point them
333-
if let proxyTo = try? await Swiftly.currentPlatform.findSwiftlyBin(ctx) {
334-
// Ensure swiftly doesn't overwrite any existing executables without getting confirmation first.
335-
let swiftlyBinDir = Swiftly.currentPlatform.swiftlyBinDir(ctx)
336-
let swiftlyBinDirContents =
337-
(try? await fs.ls(atPath: swiftlyBinDir)) ?? [String]()
338-
let toolchainBinDir = Swiftly.currentPlatform.findToolchainBinDir(ctx, version)
339-
let toolchainBinDirContents = try await fs.ls(atPath: toolchainBinDir)
340-
341-
var existingProxies: [String] = []
342-
343-
for bin in swiftlyBinDirContents {
344-
do {
345-
let linkTarget = try await fs.readlink(atPath: swiftlyBinDir / bin)
346-
if linkTarget == proxyTo {
347-
existingProxies.append(bin)
348-
}
349-
} catch { continue }
350-
}
351-
352-
let overwrite = Set(toolchainBinDirContents).subtracting(existingProxies).intersection(
353-
swiftlyBinDirContents)
354-
if !overwrite.isEmpty && !assumeYes {
355-
await ctx.print("The following existing executables will be overwritten:")
356-
357-
for executable in overwrite {
358-
await ctx.print(" \(swiftlyBinDir / executable)")
359-
}
360-
361-
<<<<<<< HEAD
362-
guard await ctx.promptForConfirmation(defaultBehavior: false) else {
363-
throw SwiftlyError(message: "Toolchain installation has been cancelled")
364-
}
365-
}
366-
367-
if verbose {
368-
await ctx.print("Setting up toolchain proxies...")
369-
}
370-
371-
let proxiesToCreate = Set(toolchainBinDirContents).subtracting(swiftlyBinDirContents).union(
372-
overwrite)
373-
374-
for p in proxiesToCreate {
375-
let proxy = Swiftly.currentPlatform.swiftlyBinDir(ctx) / p
376-
377-
if try await fs.exists(atPath: proxy) {
378-
try await fs.remove(atPath: proxy)
379-
}
380-
381-
try await fs.symlink(atPath: proxy, linkPath: proxyTo)
382-
383-
pathChanged = true
384-
}
385-
}
386-
387-
config.installedToolchains.insert(version)
388-
389-
=======
390-
let downloadedMiB = Double(progress.receivedBytes) / (1024.0 * 1024.0)
391-
let totalMiB = Double(progress.totalBytes!) / (1024.0 * 1024.0)
392-
393-
lastUpdate = Date()
394-
395-
animation.update(
396-
step: progress.receivedBytes,
397-
total: progress.totalBytes!,
398-
text:
399-
"Downloaded \(String(format: "%.1f", downloadedMiB)) MiB of \(String(format: "%.1f", totalMiB)) MiB"
400-
)
401-
}
402-
)
403-
} catch let notFound as DownloadNotFoundError {
404-
throw SwiftlyError(message: "\(version) does not exist at URL \(notFound.url), exiting")
405-
} catch {
406-
animation.complete(success: false)
407-
throw error
408-
}
409-
animation.complete(success: true)
410-
411-
if verifySignature {
412-
try await Swiftly.currentPlatform.verifyToolchainSignature(
347+
let pathChanged = try await Self.setupProxies(
413348
ctx,
414-
toolchainFile: toolchainFile,
415-
archive: tmpFile,
416-
verbose: verbose
349+
version: version,
350+
verbose: verbose,
351+
assumeYes: assumeYes
417352
)
418-
}
419-
420-
try await Swiftly.currentPlatform.install(ctx, from: tmpFile, version: version, verbose: verbose)
421-
422-
let pathChanged = try await Self.setupProxies(
423-
ctx,
424-
version: version,
425-
verbose: verbose,
426-
assumeYes: assumeYes
427-
)
428-
429-
config.installedToolchains.insert(version)
430353

431-
try config.save(ctx)
432-
433-
// If this is the first installed toolchain, mark it as in-use regardless of whether the
434-
// --use argument was provided.
435-
if useInstalledToolchain {
436-
try await Use.execute(ctx, version, globalDefault: false, &config)
437-
}
354+
config.installedToolchains.insert(version)
438355

439-
// We always update the global default toolchain if there is none set. This could
440-
// be the only toolchain that is installed, which makes it the only choice.
441-
if config.inUse == nil {
442-
config.inUse = version
443-
>>>>>>> 5d7eb2d (Add ability to temporarily disable swiftly)
444356
try config.save(ctx)
445357

446358
// If this is the first installed toolchain, mark it as in-use regardless of whether the

Sources/Swiftly/Link.swift

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,10 @@ struct Link: SwiftlyCommand {
2323
}
2424

2525
mutating func run(_ ctx: SwiftlyCoreContext) async throws {
26-
try validateSwiftly(ctx)
27-
28-
var config = try Config.load(ctx)
26+
var config = try await validatedConfig(ctx)
2927
let toolchainVersion = try await Install.determineToolchainVersion(
3028
ctx,
31-
version: nil,
29+
version: config.inUse?.name,
3230
config: &config
3331
)
3432

@@ -40,7 +38,11 @@ struct Link: SwiftlyCommand {
4038
)
4139

4240
if pathChanged {
43-
await ctx.print(Messages.refreshShell)
41+
await ctx.print("""
42+
Linked swiftly to \(toolchainVersion.name).
43+
44+
\(Messages.refreshShell)
45+
""")
4446
}
4547
}
4648
}

Sources/Swiftly/List.swift

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,13 +38,11 @@ struct List: SwiftlyCommand {
3838
}
3939

4040
mutating func run(_ ctx: SwiftlyCoreContext) async throws {
41-
try await validateSwiftly(ctx)
41+
var config = try await validatedConfig(ctx)
4242
let selector = try self.toolchainSelector.map { input in
4343
try ToolchainSelector(parsing: input)
4444
}
4545

46-
var config = try await Config.load(ctx)
47-
4846
let toolchains = config.listInstalledToolchains(selector: selector).sorted { $0 > $1 }
4947
let (inUse, _) = try await selectToolchain(ctx, config: &config)
5048

Sources/Swiftly/ListAvailable.swift

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,13 +44,11 @@ struct ListAvailable: SwiftlyCommand {
4444
}
4545

4646
mutating func run(_ ctx: SwiftlyCoreContext) async throws {
47-
try await validateSwiftly(ctx)
47+
var config = try await validatedConfig(ctx)
4848
let selector = try self.toolchainSelector.map { input in
4949
try ToolchainSelector(parsing: input)
5050
}
5151

52-
var config = try await Config.load(ctx)
53-
5452
let tc: [ToolchainVersion]
5553

5654
switch selector {

Sources/Swiftly/Run.swift

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -58,15 +58,13 @@ struct Run: SwiftlyCommand {
5858
}
5959

6060
mutating func run(_ ctx: SwiftlyCoreContext) async throws {
61-
try await validateSwiftly(ctx)
61+
var config = try await validatedConfig(ctx)
6262

6363
// Handle the specific case where help is requested of the run subcommand
6464
if command == ["--help"] {
6565
throw CleanExit.helpRequest(self)
6666
}
6767

68-
var config = try await Config.load(ctx)
69-
7068
let (command, selector) = try Self.extractProxyArguments(command: self.command)
7169

7270
let toolchain: ToolchainVersion?

Sources/Swiftly/SelfUpdate.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ struct SelfUpdate: SwiftlyCommand {
2121
}
2222

2323
mutating func run(_ ctx: SwiftlyCoreContext) async throws {
24-
try await validateSwiftly(ctx)
24+
try await validatedConfig(ctx)
2525

2626
let swiftlyBin = Swiftly.currentPlatform.swiftlyBinDir(ctx) / "swiftly"
2727
guard try await fs.exists(atPath: swiftlyBin) else {

Sources/Swiftly/Swiftly.swift

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import SystemPackage
1010

1111
typealias fs = SwiftlyCore.FileSystem
1212

13-
extension FilePath: ExpressibleByArgument {
13+
extension FilePath: @retroactive ExpressibleByArgument {
1414
public init?(argument: String) {
1515
self.init(argument)
1616
}
@@ -95,7 +95,8 @@ extension Data {
9595
}
9696

9797
extension SwiftlyCommand {
98-
public mutating func validateSwiftly(_ ctx: SwiftlyCoreContext) async throws {
98+
@discardableResult
99+
public mutating func validatedConfig(_ ctx: SwiftlyCoreContext) async throws -> Config {
99100
for requiredDir in Swiftly.requiredDirectories(ctx) {
100101
guard try await fs.exists(atPath: requiredDir) else {
101102
do {
@@ -107,7 +108,7 @@ extension SwiftlyCommand {
107108
}
108109
}
109110

110-
// Verify that the configuration exists and can be loaded
111-
_ = try await Config.load(ctx)
111+
// Verifies that the configuration exists and can be loaded
112+
return try await Config.load(ctx)
112113
}
113114
}

Sources/Swiftly/Uninstall.swift

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,7 @@ struct Uninstall: SwiftlyCommand {
4848
}
4949

5050
mutating func run(_ ctx: SwiftlyCoreContext) async throws {
51-
try await validateSwiftly(ctx)
52-
let startingConfig = try await Config.load(ctx)
51+
let startingConfig = try await validatedConfig(ctx)
5352

5453
let toolchains: [ToolchainVersion]
5554
if self.toolchain == "all" {

Sources/Swiftly/Unlink.swift

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import ArgumentParser
22
import Foundation
33
import SwiftlyCore
4+
import SystemPackage
45

56
struct Unlink: SwiftlyCommand {
67
public static let configuration = CommandConfiguration(
@@ -25,25 +26,25 @@ struct Unlink: SwiftlyCommand {
2526
}
2627

2728
mutating func run(_ ctx: SwiftlyCoreContext) async throws {
28-
try validateSwiftly(ctx)
29+
try await validatedConfig(ctx)
2930

3031
var pathChanged = false
31-
if let proxyTo = try? Swiftly.currentPlatform.findSwiftlyBin(ctx) {
32+
if let proxyTo = try? await Swiftly.currentPlatform.findSwiftlyBin(ctx) {
3233
let swiftlyBinDir = Swiftly.currentPlatform.swiftlyBinDir(ctx)
33-
let swiftlyBinDirContents = (try? FileManager.default.contentsOfDirectory(atPath: swiftlyBinDir.path)) ?? [String]()
34+
let swiftlyBinDirContents = (try? FileManager.default.contentsOfDirectory(atPath: swiftlyBinDir.string)) ?? [String]()
3435

3536
let existingProxies = swiftlyBinDirContents.filter { bin in
3637
do {
37-
let linkTarget = try FileManager.default.destinationOfSymbolicLink(atPath: swiftlyBinDir.appendingPathComponent(bin).path)
38-
return linkTarget == proxyTo
38+
let linkTarget = try FileManager.default.destinationOfSymbolicLink(atPath: (swiftlyBinDir / bin).string)
39+
return linkTarget == proxyTo.string
3940
} catch { return false }
4041
}
4142

4243
for p in existingProxies {
43-
let proxy = Swiftly.currentPlatform.swiftlyBinDir(ctx).appendingPathComponent(p)
44+
let proxy = Swiftly.currentPlatform.swiftlyBinDir(ctx) / p
4445

45-
if proxy.fileExists() {
46-
try FileManager.default.removeItem(at: proxy)
46+
if try await fs.exists(atPath: proxy) {
47+
try await fs.remove(atPath: proxy)
4748
pathChanged = true
4849
}
4950
}

0 commit comments

Comments
 (0)