Repository: swift-server/swiftly (swift)

Issue:
Provide option to use the newly installed toolchain
The `install` command should accept an argument (e.g. `--use`) which will automatically use the new toolchain if installation succeeds. This already happens by default if there are no toolchains installed yet, but this would be very useful in other cases too.

Pull request description:
Closes #71

Patch:
diff --git a/Sources/Swiftly/Install.swift b/Sources/Swiftly/Install.swift
index 922f358c..bd5e1f68 100644
--- a/Sources/Swiftly/Install.swift
+++ b/Sources/Swiftly/Install.swift
@@ -43,6 +43,9 @@ struct Install: SwiftlyCommand {
     ))
     var version: String
 
+    @Flag(name: .shortAndLong, help: "Mark the newly installed toolchain as in-use.")
+    var use: Bool = false
+
     @Option(help: ArgumentHelp(
         "A GitHub authentiation token to use for any GitHub API requests.",
         discussion: """
@@ -56,7 +59,7 @@ struct Install: SwiftlyCommand {
     public var httpClient = SwiftlyHTTPClient()
 
     private enum CodingKeys: String, CodingKey {
-        case version, token
+        case version, token, use
     }
 
     mutating func run() async throws {
@@ -64,13 +67,14 @@ struct Install: SwiftlyCommand {
         self.httpClient.githubToken = self.token
         let toolchainVersion = try await self.resolve(selector: selector)
         var config = try Config.load()
-        try await Self.execute(version: toolchainVersion, &config, self.httpClient)
+        try await Self.execute(version: toolchainVersion, &config, self.httpClient, useInstalledToolchain: self.use)
     }
 
     internal static func execute(
         version: ToolchainVersion,
         _ config: inout Config,
-        _ httpClient: SwiftlyHTTPClient
+        _ httpClient: SwiftlyHTTPClient,
+        useInstalledToolchain: Bool
     ) async throws {
         guard !config.installedToolchains.contains(version) else {
             SwiftlyCore.print("\(version) is already installed, exiting.")
@@ -168,8 +172,9 @@ struct Install: SwiftlyCommand {
         config.installedToolchains.insert(version)
         try config.save()
 
-        // If this is the first installed toolchain, mark it as in-use.
-        if config.inUse == nil {
+        // If this is the first installed toolchain, mark it as in-use regardless of whether the
+        // --use argument was provided.
+        if useInstalledToolchain || config.inUse == nil {
             try await Use.execute(version, &config)
         }
 
diff --git a/Sources/Swiftly/Update.swift b/Sources/Swiftly/Update.swift
index 2fb4c138..96d6c6d5 100644
--- a/Sources/Swiftly/Update.swift
+++ b/Sources/Swiftly/Update.swift
@@ -100,11 +100,12 @@ struct Update: SwiftlyCommand {
             }
         }
 
-        try await Install.execute(version: newToolchain, &config, self.httpClient)
-
-        if config.inUse == parameters.oldToolchain {
-            try await Use.execute(newToolchain, &config)
-        }
+        try await Install.execute(
+            version: newToolchain,
+            &config,
+            self.httpClient,
+            useInstalledToolchain: config.inUse == parameters.oldToolchain
+        )
 
         try await Uninstall.execute(parameters.oldToolchain, &config)
         SwiftlyCore.print("Successfully updated \(parameters.oldToolchain) ⟶ \(newToolchain)")


Test patch:
diff --git a/Tests/SwiftlyTests/InstallTests.swift b/Tests/SwiftlyTests/InstallTests.swift
index 90495e35..04fc5ee6 100644
--- a/Tests/SwiftlyTests/InstallTests.swift
+++ b/Tests/SwiftlyTests/InstallTests.swift
@@ -255,4 +255,16 @@ final class InstallTests: SwiftlyTests {
             try await self.validateInUse(expected: ToolchainVersion(major: 5, minor: 7, patch: 0))
         }
     }
+
+    /// Verify that the installed toolchain will be marked as in-use if the --use flag is specified.
+    func testInstallUseFlag() async throws {
+        try await self.withTestHome {
+            try await self.installMockedToolchain(toolchain: Self.oldStable)
+            var use = try self.parseCommand(Use.self, ["use", Self.oldStable.name])
+            try await use.run()
+            try await validateInUse(expected: Self.oldStable)
+            try await self.installMockedToolchain(selector: Self.newStable.name, args: ["--use"])
+            try await self.validateInUse(expected: Self.newStable)
+        }
+    }
 }
diff --git a/Tests/SwiftlyTests/SwiftlyTests.swift b/Tests/SwiftlyTests/SwiftlyTests.swift
index 266f147b..605a5631 100644
--- a/Tests/SwiftlyTests/SwiftlyTests.swift
+++ b/Tests/SwiftlyTests/SwiftlyTests.swift
@@ -172,8 +172,8 @@ class SwiftlyTests: XCTestCase {
     /// in its bin directory.
     ///
     /// When executed, the mocked executables will simply print the toolchain version and return.
-    func installMockedToolchain(selector: String, executables: [String]? = nil) async throws {
-        var install = try self.parseCommand(Install.self, ["install", "\(selector)"])
+    func installMockedToolchain(selector: String, args: [String] = [], executables: [String]? = nil) async throws {
+        var install = try self.parseCommand(Install.self, ["install", "\(selector)"] + args)
         install.httpClient = SwiftlyHTTPClient(toolchainDownloader: MockToolchainDownloader(executables: executables))
         try await install.run()
     }