From 14215e31cdc21e827b4bd03da1578e2f391dc663 Mon Sep 17 00:00:00 2001 From: Priyambada Roul Date: Mon, 14 Jul 2025 23:43:22 +0530 Subject: [PATCH 1/7] Add Swiftly toolchain management --- src/toolchain/swiftly.ts | 4 ++++ test/unit-tests/toolchain/swiftly.test.ts | 7 +++++++ 2 files changed, 11 insertions(+) diff --git a/src/toolchain/swiftly.ts b/src/toolchain/swiftly.ts index 1e2843cd8..a9f6ba836 100644 --- a/src/toolchain/swiftly.ts +++ b/src/toolchain/swiftly.ts @@ -24,7 +24,9 @@ const ListResult = z.object({ toolchains: z.array( z.object({ inUse: z.boolean(), + installed: z.boolean(), isDefault: z.boolean(), + name: z.string(), version: z.discriminatedUnion("type", [ z.object({ major: z.number().optional(), @@ -243,3 +245,5 @@ export class Swiftly { return JSON.parse(swiftlyConfigRaw); } } + +export const swiftly = new Swiftly(); diff --git a/test/unit-tests/toolchain/swiftly.test.ts b/test/unit-tests/toolchain/swiftly.test.ts index fa48bea81..f89d2279b 100644 --- a/test/unit-tests/toolchain/swiftly.test.ts +++ b/test/unit-tests/toolchain/swiftly.test.ts @@ -13,6 +13,7 @@ //===----------------------------------------------------------------------===// import { expect } from "chai"; +import * as sinon from "sinon"; import { Swiftly } from "../../../src/toolchain/swiftly"; import * as utilities from "../../../src/utilities/utilities"; import { mockGlobalModule, mockGlobalValue } from "../../MockUtils"; @@ -38,7 +39,9 @@ suite("Swiftly Unit Tests", () => { toolchains: [ { inUse: true, + installed: true, isDefault: true, + name: "swift-5.9.0-RELEASE", version: { major: 5, minor: 9, @@ -49,7 +52,9 @@ suite("Swiftly Unit Tests", () => { }, { inUse: false, + installed: true, isDefault: false, + name: "swift-5.8.0-RELEASE", version: { major: 5, minor: 8, @@ -60,7 +65,9 @@ suite("Swiftly Unit Tests", () => { }, { inUse: false, + installed: false, isDefault: false, + name: "swift-DEVELOPMENT-SNAPSHOT-2023-10-15-a", version: { major: 5, minor: 10, From 6c466549fc7b6525d3759e621a263a68839098b4 Mon Sep 17 00:00:00 2001 From: Priyambada Roul Date: Wed, 16 Jul 2025 18:27:13 +0530 Subject: [PATCH 2/7] refactoring changes --- src/toolchain/swiftly.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/toolchain/swiftly.ts b/src/toolchain/swiftly.ts index a9f6ba836..249055c73 100644 --- a/src/toolchain/swiftly.ts +++ b/src/toolchain/swiftly.ts @@ -220,8 +220,7 @@ export class Swiftly { const error = err as ExecFileError; // Its possible the toolchain in .swift-version is misconfigured or doesn't exist. void vscode.window.showErrorMessage( - `Failed to load toolchain from Swiftly: ${error.stderr}` - ); + `Failed to load toolchain from Swiftly: ${error.stderr}`); } } } From 72a23c91457f8d143ee4ca279077976ecf39e968 Mon Sep 17 00:00:00 2001 From: Priyambada Roul Date: Wed, 16 Jul 2025 22:57:03 +0530 Subject: [PATCH 3/7] refactoring changes --- test/unit-tests/toolchain/swiftly.test.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/test/unit-tests/toolchain/swiftly.test.ts b/test/unit-tests/toolchain/swiftly.test.ts index f89d2279b..ceeaa48f7 100644 --- a/test/unit-tests/toolchain/swiftly.test.ts +++ b/test/unit-tests/toolchain/swiftly.test.ts @@ -13,7 +13,6 @@ //===----------------------------------------------------------------------===// import { expect } from "chai"; -import * as sinon from "sinon"; import { Swiftly } from "../../../src/toolchain/swiftly"; import * as utilities from "../../../src/utilities/utilities"; import { mockGlobalModule, mockGlobalValue } from "../../MockUtils"; From b31d615c02ac646c4baddca2d369eb56cb290165 Mon Sep 17 00:00:00 2001 From: Priyambada Roul Date: Wed, 16 Jul 2025 23:30:15 +0530 Subject: [PATCH 4/7] refactoring changes --- src/toolchain/swiftly.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/toolchain/swiftly.ts b/src/toolchain/swiftly.ts index 249055c73..32d5dce40 100644 --- a/src/toolchain/swiftly.ts +++ b/src/toolchain/swiftly.ts @@ -244,5 +244,3 @@ export class Swiftly { return JSON.parse(swiftlyConfigRaw); } } - -export const swiftly = new Swiftly(); From b8042842fb8603783fcea18e490121d9e1992635 Mon Sep 17 00:00:00 2001 From: Priyambada Roul Date: Thu, 17 Jul 2025 10:41:45 +0530 Subject: [PATCH 5/7] refactoring changes --- src/toolchain/swiftly.ts | 6 ++++-- test/unit-tests/toolchain/swiftly.test.ts | 10 ++++++---- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/toolchain/swiftly.ts b/src/toolchain/swiftly.ts index 32d5dce40..1eea8c4ff 100644 --- a/src/toolchain/swiftly.ts +++ b/src/toolchain/swiftly.ts @@ -127,7 +127,8 @@ export class Swiftly { const response = ListResult.parse(JSON.parse(stdout)); return response.toolchains.map(t => t.version.name); } catch (error) { - outputChannel?.appendLine(`Failed to retrieve Swiftly installations: ${error}`); + outputChannel?.appendLine( + `Failed to retrieve Swiftly installations : ${error}`); return []; } } @@ -220,7 +221,8 @@ export class Swiftly { const error = err as ExecFileError; // Its possible the toolchain in .swift-version is misconfigured or doesn't exist. void vscode.window.showErrorMessage( - `Failed to load toolchain from Swiftly: ${error.stderr}`); + `Failed to load toolchain from Swiftly: ${error.stderr}` + ); } } } diff --git a/test/unit-tests/toolchain/swiftly.test.ts b/test/unit-tests/toolchain/swiftly.test.ts index ceeaa48f7..e1bbfee47 100644 --- a/test/unit-tests/toolchain/swiftly.test.ts +++ b/test/unit-tests/toolchain/swiftly.test.ts @@ -79,10 +79,12 @@ suite("Swiftly Unit Tests", () => { ], }; - mockUtilities.execFile.withArgs("swiftly", ["list", "--format=json"]).resolves({ - stdout: JSON.stringify(jsonOutput), - stderr: "", - }); + mockUtilities.execFile + .withArgs("swiftly", ["list", "--format=json"]) + .resolves({ + stdout: JSON.stringify(jsonOutput), + stderr: "", + }); const result = await Swiftly.listAvailableToolchains(); From 81c8a22fa123cce504639cf9b728cb0848d8e047 Mon Sep 17 00:00:00 2001 From: Priyambada Roul Date: Thu, 17 Jul 2025 12:08:10 +0530 Subject: [PATCH 6/7] Detect if swiftly is already installed --- src/toolchain/swiftly.ts | 20 +++++++++++++++++++- src/ui/ToolchainSelection.ts | 2 +- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/src/toolchain/swiftly.ts b/src/toolchain/swiftly.ts index 1eea8c4ff..c463e1531 100644 --- a/src/toolchain/swiftly.ts +++ b/src/toolchain/swiftly.ts @@ -158,7 +158,7 @@ export class Swiftly { } } - private static isSupported() { + public static isSupported() { return process.platform === "linux" || process.platform === "darwin"; } @@ -245,4 +245,22 @@ export class Swiftly { ); return JSON.parse(swiftlyConfigRaw); } + + public static async isInstalled() { + + if(!Swiftly.isSupported()) { + return false; + } + + try { + await Swiftly.version(); + return true; + } catch (error) { + if (error instanceof ExecFileError && 'code' in error && error.code === "ENOENT") { + return false; + } + throw error; + } + + } } diff --git a/src/ui/ToolchainSelection.ts b/src/ui/ToolchainSelection.ts index 9cff98ebb..231a41872 100644 --- a/src/ui/ToolchainSelection.ts +++ b/src/ui/ToolchainSelection.ts @@ -257,7 +257,7 @@ async function getQuickPickItems( } // Various actions that the user can perform (e.g. to install new toolchains) const actionItems: ActionItem[] = []; - if (process.platform === "linux" || process.platform === "darwin") { + if (Swiftly.isSupported() && !(await Swiftly.isInstalled())) { const platformName = process.platform === "linux" ? "Linux" : "macOS"; actionItems.push({ type: "action", From fba73533307907ad0e9f6c2ae06c738e5457fa87 Mon Sep 17 00:00:00 2001 From: Priyambada Roul Date: Thu, 17 Jul 2025 12:25:03 +0530 Subject: [PATCH 7/7] Detect if swiftly is already installed --- src/toolchain/swiftly.ts | 6 ++-- test/unit-tests/toolchain/swiftly.test.ts | 42 +++++++++++++++++++++++ 2 files changed, 44 insertions(+), 4 deletions(-) diff --git a/src/toolchain/swiftly.ts b/src/toolchain/swiftly.ts index c463e1531..8e64531e6 100644 --- a/src/toolchain/swiftly.ts +++ b/src/toolchain/swiftly.ts @@ -247,8 +247,7 @@ export class Swiftly { } public static async isInstalled() { - - if(!Swiftly.isSupported()) { + if (!Swiftly.isSupported()) { return false; } @@ -256,11 +255,10 @@ export class Swiftly { await Swiftly.version(); return true; } catch (error) { - if (error instanceof ExecFileError && 'code' in error && error.code === "ENOENT") { + if (error instanceof ExecFileError && "code" in error && error.code === "ENOENT") { return false; } throw error; } - } } diff --git a/test/unit-tests/toolchain/swiftly.test.ts b/test/unit-tests/toolchain/swiftly.test.ts index e1bbfee47..1219c481c 100644 --- a/test/unit-tests/toolchain/swiftly.test.ts +++ b/test/unit-tests/toolchain/swiftly.test.ts @@ -110,4 +110,46 @@ suite("Swiftly Unit Tests", () => { expect(mockUtilities.execFile).not.have.been.called; }); }); + + suite("isInstalled", () => { + test("should return false when platform is not supported", async () => { + mockedPlatform.setValue("win32"); + + const result = await Swiftly.isInstalled(); + + expect(result).to.be.false; + expect(mockUtilities.execFile).not.have.been.called; + }); + + test("should return true when swiftly is installed", async () => { + mockedPlatform.setValue("darwin"); + + mockUtilities.execFile.withArgs("swiftly", ["--version"]).resolves({ + stdout: "1.1.0\n", + stderr: "", + }); + + const result = await Swiftly.isInstalled(); + + expect(result).to.be.true; + expect(mockUtilities.execFile).to.have.been.calledWith("swiftly", ["--version"]); + }); + + test("should throw error when swiftly command fails with non-ENOENT error", async () => { + mockedPlatform.setValue("darwin"); + + const otherError = new Error("Other error"); + + mockUtilities.execFile.withArgs("swiftly", ["--version"]).rejects(otherError); + + try { + await Swiftly.isInstalled(); + expect.fail("Should have thrown an error"); + } catch (error) { + expect(error).to.equal(otherError); + } + + expect(mockUtilities.execFile).to.have.been.calledWith("swiftly", ["--version"]); + }); + }); });