From 7ed943d46f6e6ee6b86a0e37ff96dbeb68f6ccab Mon Sep 17 00:00:00 2001 From: Reinier Criel Date: Wed, 15 Apr 2026 09:19:20 -0700 Subject: [PATCH] Fix Windows bash --- .../supported-shells/bash.js | 74 ++++++++++++++++++- .../supported-shells/bash.spec.js | 34 ++++++++- 2 files changed, 103 insertions(+), 5 deletions(-) diff --git a/packages/safe-chain/src/shell-integration/supported-shells/bash.js b/packages/safe-chain/src/shell-integration/supported-shells/bash.js index e106928..34dcde7 100644 --- a/packages/safe-chain/src/shell-integration/supported-shells/bash.js +++ b/packages/safe-chain/src/shell-integration/supported-shells/bash.js @@ -46,10 +46,11 @@ function teardown(tools) { function setup() { const startupFile = getStartupFile(); + const scriptsDir = getShellScriptsDir(); addLineToFile( startupFile, - `source ${path.join(getScriptsDir(), "init-posix.sh")} # Safe-chain bash initialization script`, + `source ${path.posix.join(scriptsDir, "init-posix.sh")} # Safe-chain bash initialization script`, eol ); @@ -96,6 +97,51 @@ function windowsFixPath(path) { } } +function getShellScriptsDir() { + return toBashPath(getScriptsDir()); +} + +/** + * @param {string} path + * + * @returns {string} + */ +function toBashPath(path) { + try { + if (os.platform() !== "win32") { + return path.replace(/\\/g, "/"); + } + + const directWindowsPath = windowsPathToBashPath(path); + if (directWindowsPath) { + return directWindowsPath; + } + + if (hasCygpath()) { + return cygpathu(path); + } + + return path.replace(/\\/g, "/"); + } catch { + return path.replace(/\\/g, "/"); + } +} + +/** + * @param {string} path + * + * @returns {string | undefined} + */ +function windowsPathToBashPath(path) { + const match = /^([A-Za-z]):[\\/](.*)$/.exec(path); + if (!match) { + return undefined; + } + + const [, driveLetter, rest] = match; + return `/${driveLetter.toLowerCase()}/${rest.replace(/\\/g, "/")}`; +} + function hasCygpath() { try { var result = spawnSync("where", ["cygpath"], { shell: executableName }); @@ -125,18 +171,40 @@ function cygpathw(path) { } } +/** + * @param {string} path + * + * @returns {string} + */ +function cygpathu(path) { + try { + var result = spawnSync("cygpath", ["-u", path], { + encoding: "utf8", + shell: executableName, + }); + if (result.status === 0) { + return result.stdout.trim(); + } + return path.replace(/\\/g, "/"); + } catch { + return path.replace(/\\/g, "/"); + } +} + function getManualTeardownInstructions() { + const scriptsDir = getShellScriptsDir(); return [ `Remove the following line from your ~/.bashrc file:`, - ` source ${path.join(getScriptsDir(), "init-posix.sh")}`, + ` source ${path.posix.join(scriptsDir, "init-posix.sh")}`, `Then restart your terminal or run: source ~/.bashrc`, ]; } function getManualSetupInstructions() { + const scriptsDir = getShellScriptsDir(); return [ `Add the following line to your ~/.bashrc file:`, - ` source ${path.join(getScriptsDir(), "init-posix.sh")}`, + ` source ${path.posix.join(scriptsDir, "init-posix.sh")}`, `Then restart your terminal or run: source ~/.bashrc`, ]; } diff --git a/packages/safe-chain/src/shell-integration/supported-shells/bash.spec.js b/packages/safe-chain/src/shell-integration/supported-shells/bash.spec.js index a6b09a0..ac80d1f 100644 --- a/packages/safe-chain/src/shell-integration/supported-shells/bash.spec.js +++ b/packages/safe-chain/src/shell-integration/supported-shells/bash.spec.js @@ -9,6 +9,7 @@ describe("Bash shell integration", () => { let mockStartupFile; let bash; let windowsCygwinPath = ""; + let mockScriptsDir = "/test-home/.safe-chain/scripts"; let platform = "linux"; beforeEach(async () => { @@ -37,7 +38,7 @@ describe("Bash shell integration", () => { mock.module("../../config/safeChainDir.js", { namedExports: { - getScriptsDir: () => "/test-home/.safe-chain/scripts", + getScriptsDir: () => mockScriptsDir, }, }); @@ -67,6 +68,17 @@ describe("Bash shell integration", () => { stdout: windowsCygwinPath + "\n", }; } + + if ( + command === "cygpath" && + args[0] === "-u" && + args[1] === mockScriptsDir + ) { + return { + status: 0, + stdout: "/c/test-home/.safe-chain/scripts\n", + }; + } }, }, }); @@ -93,6 +105,7 @@ describe("Bash shell integration", () => { // Reset mocks mock.reset(); + mockScriptsDir = "/test-home/.safe-chain/scripts"; platform = "linux"; }); @@ -135,7 +148,24 @@ describe("Bash shell integration", () => { const content = fs.readFileSync(windowsCygwinPath, "utf-8"); assert.ok( content.includes( - "source /test-home/.safe-chain/scripts/init-posix.sh # Safe-chain bash initialization script" + "source /c/test-home/.safe-chain/scripts/init-posix.sh # Safe-chain bash initialization script" + ) + ); + }); + + it("should write a bash-compatible scripts path on Windows", () => { + platform = "win32"; + windowsCygwinPath = mockStartupFile; + mockScriptsDir = "C:\\test-home\\.safe-chain\\scripts"; + mockStartupFile = "DUMMY"; + + const result = bash.setup(); + assert.strictEqual(result, true); + + const content = fs.readFileSync(windowsCygwinPath, "utf-8"); + assert.ok( + content.includes( + "source /c/test-home/.safe-chain/scripts/init-posix.sh # Safe-chain bash initialization script" ) ); });