diff --git a/common/autoinstallers/rush-plugins/package.json b/common/autoinstallers/rush-plugins/package.json index 9662e5671b..a28409e55e 100644 --- a/common/autoinstallers/rush-plugins/package.json +++ b/common/autoinstallers/rush-plugins/package.json @@ -4,6 +4,7 @@ "private": true, "pnpm": { "overrides": { + "fast-xml-parser": "5.3.7" } }, "dependencies": { diff --git a/common/autoinstallers/rush-plugins/pnpm-lock.yaml b/common/autoinstallers/rush-plugins/pnpm-lock.yaml index f5c64ea3e1..e172337f04 100644 --- a/common/autoinstallers/rush-plugins/pnpm-lock.yaml +++ b/common/autoinstallers/rush-plugins/pnpm-lock.yaml @@ -4,6 +4,9 @@ settings: autoInstallPeers: true excludeLinksFromLockfile: false +overrides: + fast-xml-parser: 5.3.7 + importers: .: @@ -142,8 +145,8 @@ packages: resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==} engines: {node: '>= 14'} - minimatch@3.1.2: - resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} + minimatch@3.1.3: + resolution: {integrity: sha512-M2GCs7Vk83NxkUyQV1bkABc4yxgz9kILhHImZiBPAZ9ybuvCb0/H7lEl5XvIg3g+9d4eNotkZA5IWwYl0tibaA==} ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} @@ -195,7 +198,7 @@ snapshots: '@actions/glob@0.5.1': dependencies: '@actions/core': 2.0.3 - minimatch: 3.1.2 + minimatch: 3.1.3 '@actions/http-client@3.0.2': dependencies: @@ -377,7 +380,7 @@ snapshots: transitivePeerDependencies: - supports-color - minimatch@3.1.2: + minimatch@3.1.3: dependencies: brace-expansion: 1.1.12 diff --git a/common/config/rush/.pnpmfile.cjs b/common/config/rush/.pnpmfile.cjs index e5da24e28a..2d95103983 100644 --- a/common/config/rush/.pnpmfile.cjs +++ b/common/config/rush/.pnpmfile.cjs @@ -41,7 +41,7 @@ module.exports = { pkg.dependencies['eslint'] = '^9.27.0'; } if (pkg.dependencies['fast-xml-parser']) { - pkg.dependencies['fast-xml-parser'] = '5.3.6'; + pkg.dependencies['fast-xml-parser'] = '5.3.7'; } if (pkg.dependencies['lodash']) { pkg.dependencies['lodash'] = '4.17.23'; @@ -92,7 +92,7 @@ module.exports = { pkg.devDependencies['eslint'] = '^9.27.0'; } if (pkg.devDependencies['fast-xml-parser']) { - pkg.devDependencies['fast-xml-parser'] = '5.3.6'; + pkg.devDependencies['fast-xml-parser'] = '5.3.7'; } if (pkg.devDependencies['lodash']) { pkg.devDependencies['lodash'] = '4.17.23'; diff --git a/common/config/rush/pnpm-lock.yaml b/common/config/rush/pnpm-lock.yaml index fc8c007d01..e787af1860 100644 --- a/common/config/rush/pnpm-lock.yaml +++ b/common/config/rush/pnpm-lock.yaml @@ -3830,8 +3830,8 @@ importers: specifier: 16.5.0 version: 16.5.0 fast-xml-parser: - specifier: 5.3.6 - version: 5.3.6 + specifier: 5.3.7 + version: 5.3.7 find-process: specifier: 1.4.10 version: 1.4.10 @@ -4132,8 +4132,8 @@ importers: specifier: 1.0.20 version: 1.0.20 fast-xml-parser: - specifier: 5.3.6 - version: 5.3.6 + specifier: 5.3.7 + version: 5.3.7 lodash: specifier: 4.17.23 version: 4.17.23 @@ -15918,8 +15918,8 @@ packages: fast-uri@3.1.0: resolution: {integrity: sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==} - fast-xml-parser@5.3.6: - resolution: {integrity: sha512-QNI3sAvSvaOiaMl8FYU4trnEzCwiRr8XMWgAHzlrWpTSj+QaCSvOf1h82OEP1s4hiAXhnbXSyFWCf4ldZzZRVA==} + fast-xml-parser@5.3.7: + resolution: {integrity: sha512-JzVLro9NQv92pOM/jTCR6mHlJh2FGwtomH8ZQjhFj/R29P2Fnj38OgPJVtcvYw6SuKClhgYuwUZf5b3rd8u2mA==} hasBin: true fastest-levenshtein@1.0.16: @@ -25885,7 +25885,7 @@ snapshots: '@smithy/smithy-client': 4.11.6 '@smithy/types': 4.12.0 '@smithy/util-middleware': 4.2.8 - fast-xml-parser: 5.3.6 + fast-xml-parser: 5.3.7 tslib: 2.8.1 '@aws-sdk/credential-provider-env@3.816.0': @@ -26713,7 +26713,7 @@ snapshots: '@babel/plugin-proposal-object-rest-spread@7.12.1(@babel/core@7.12.9)': dependencies: '@babel/core': 7.12.9 - '@babel/helper-plugin-utils': 7.10.4 + '@babel/helper-plugin-utils': 7.28.6 '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.12.9) '@babel/plugin-transform-parameters': 7.27.7(@babel/core@7.12.9) @@ -46351,7 +46351,7 @@ snapshots: fast-uri@3.1.0: {} - fast-xml-parser@5.3.6: + fast-xml-parser@5.3.7: dependencies: strnum: 2.1.2 @@ -49664,7 +49664,7 @@ snapshots: dependencies: browser-resolve: 1.11.3 is-builtin-module: 1.0.0 - resolve: 1.6.0 + resolve: 1.22.11 jest-resolve@22.4.3: dependencies: diff --git a/workspaces/ballerina/ballerina-extension/CHANGELOG.md b/workspaces/ballerina/ballerina-extension/CHANGELOG.md index 90b9d90c25..070f18f63b 100644 --- a/workspaces/ballerina/ballerina-extension/CHANGELOG.md +++ b/workspaces/ballerina/ballerina-extension/CHANGELOG.md @@ -4,7 +4,13 @@ All notable changes to the **Ballerina** extension will be documented in this fi The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and this project adheres to [Semantic Versioning](https://semver.org/). -## [5.8.0](https://github.com/wso2/vscode-extensions/compare/ballerina-5.7.3...ballerina-5.8.0) - 2026-02-14 +## [5.8.1](https://github.com/wso2/vscode-extensions/compare/ballerina-integrator-1.7.0...ballerina-5.8.1) - 2026-02-25 + +### Fixed + +- **Installation** — Enhanced Windows environment detection to properly identify Ballerina distributions on Windows. + +## [5.8.0](https://github.com/wso2/vscode-extensions/compare/ballerina-5.7.3...ballerina-integrator-1.7.0) - 2026-02-14 ### Added diff --git a/workspaces/ballerina/ballerina-extension/package.json b/workspaces/ballerina/ballerina-extension/package.json index 4488343a49..506ba3973a 100644 --- a/workspaces/ballerina/ballerina-extension/package.json +++ b/workspaces/ballerina/ballerina-extension/package.json @@ -2,7 +2,7 @@ "name": "ballerina", "displayName": "Ballerina", "description": "Ballerina Language support, debugging, graphical visualization, AI-based data-mapping and many more.", - "version": "5.8.0", + "version": "5.8.1", "publisher": "wso2", "icon": "resources/images/ballerina.png", "homepage": "https://wso2.com/ballerina/vscode/docs", diff --git a/workspaces/ballerina/ballerina-extension/src/core/extension.ts b/workspaces/ballerina/ballerina-extension/src/core/extension.ts index a9f8a7e086..b7a4171974 100644 --- a/workspaces/ballerina/ballerina-extension/src/core/extension.ts +++ b/workspaces/ballerina/ballerina-extension/src/core/extension.ts @@ -1791,6 +1791,18 @@ export class BallerinaExtension { if (distPath) { break; } } } + } else if (isWindows() && !ballerinaHome) { + // On Windows, if syncEnvironment() already merged the User+Machine PATH the + // 'bal.bat version' call below will just work via PATH lookup (distPath stays + // empty). But for restricted environments (where even User + // PATH is locked, or where VSCode's inherited PATH is still stale), we run a + // proactive directory search here so that we can use an absolute path instead + // of relying on PATH resolution. + const detectedBinPath = findWindowsBallerinaPath(); + if (detectedBinPath) { + distPath = detectedBinPath; + debug(`[VERSION] Windows fallback search found Ballerina bin: ${distPath}`); + } } let exeExtension = ""; @@ -2679,6 +2691,83 @@ function updateProcessEnv(newEnv: NodeJS.ProcessEnv): void { debug("[UPDATE_ENV] Process environment update completed"); } +/** + * Searches for the Ballerina bin directory on Windows using two strategies: + * 1. Read the User-scope and Machine-scope PATH entries from the registry and look + * for a directory that contains bal.bat. + * 2. Check well-known installation directories (LOCALAPPDATA, ProgramFiles, etc.). + * + * Returns the bin directory path (with trailing separator) or an empty string when + * nothing is found. This is used as a last-resort fallback for environments where the + * process PATH was not updated (e.g. company laptops with restricted System PATH, or + * VS Code opened before the installer ran). + */ +function findWindowsBallerinaPath(): string { + debug('[WIN_BAL_FIND] Searching for Ballerina installation on Windows...'); + + // --- Strategy 1: scan PATH entries from User + Machine registry scopes --- + try { + const psCommand = + '[Environment]::GetEnvironmentVariable(\'Path\',\'Machine\') + \';\' + ' + + '[Environment]::GetEnvironmentVariable(\'Path\',\'User\')'; + const rawPaths = execSync( + `powershell.exe -NoProfile -Command "${psCommand}"`, + { encoding: 'utf8', timeout: 10000 } + ).trim(); + + debug(`[WIN_BAL_FIND] Registry PATH (Machine+User) length: ${rawPaths.length} chars`); + + const pathEntries = rawPaths.split(';').map(p => p.trim()).filter(Boolean); + for (const entry of pathEntries) { + const candidate = path.join(entry, 'bal.bat'); + if (fs.existsSync(candidate)) { + debug(`[WIN_BAL_FIND] Found bal.bat in registry PATH entry: ${entry}`); + return entry + path.sep; + } + } + debug('[WIN_BAL_FIND] bal.bat not found in registry PATH entries'); + } catch (err) { + debug(`[WIN_BAL_FIND] Failed to read registry PATH: ${err}`); + } + + // --- Strategy 2: check well-known Ballerina installation directories --- + const localAppData = process.env.LOCALAPPDATA || ''; + const programFiles = process.env.ProgramFiles || 'C:\\Program Files'; + const programFilesX86 = process.env['ProgramFiles(x86)'] || 'C:\\Program Files (x86)'; + + const searchRoots = [ + localAppData ? path.join(localAppData, 'Programs', 'Ballerina') : '', + path.join(programFiles, 'Ballerina'), + path.join(programFilesX86, 'Ballerina'), + 'C:\\Ballerina', + ].filter(Boolean); + + for (const root of searchRoots) { + const directBin = path.join(root, 'bin'); + if (fs.existsSync(path.join(directBin, 'bal.bat'))) { + debug(`[WIN_BAL_FIND] Found bal.bat in common directory: ${directBin}`); + return directBin + path.sep; + } + // Handle versioned subdirectory layout, e.g. Ballerina\ballerina-2.x.x\bin + try { + const children = fs.readdirSync(root); + for (const child of children) { + const versionedBin = path.join(root, child, 'bin'); + if (fs.existsSync(path.join(versionedBin, 'bal.bat'))) { + debug(`[WIN_BAL_FIND] Found bal.bat in versioned directory: ${versionedBin}`); + return versionedBin + path.sep; + } + } + } catch (err) { + // Directory doesn't exist or isn't readable — skip + debug(`[WIN_BAL_FIND] Failed to read directory "${root}" for versioned Ballerina installations: ${err}`); + } + } + + debug('[WIN_BAL_FIND] Ballerina installation not found via fallback search'); + return ''; +} + function getShellEnvironment(): Promise { return new Promise((resolve, reject) => { debug('[SHELL_ENV] Starting shell environment retrieval...'); @@ -2688,8 +2777,19 @@ function getShellEnvironment(): Promise { if (isWindowsPlatform) { debug('[SHELL_ENV] Windows platform detected'); - // Windows: use PowerShell to get environment - command = 'powershell.exe -Command "[Environment]::GetEnvironmentVariables(\'Process\') | ConvertTo-Json"'; + // Windows: read from registry (Machine + User scopes) so that paths added by + // a fresh Ballerina install (which goes to the User PATH registry key) are + // picked up even when VS Code's process was launched before the installation. + // We start with the current Process environment so that VS Code-internal + // variables are preserved, but we override Path with the merged registry value. + command = 'powershell.exe -NoProfile -Command "' + + '$e=[Environment]::GetEnvironmentVariables(\'Process\');' + + '$mp=[Environment]::GetEnvironmentVariable(\'Path\',\'Machine\');' + + '$up=[Environment]::GetEnvironmentVariable(\'Path\',\'User\');' + + 'if($mp -and $up){$e[\'Path\']=$mp+\';\'+$up}' + + 'elseif($mp){$e[\'Path\']=$mp}' + + 'elseif($up){$e[\'Path\']=$up};' + + '$e | ConvertTo-Json"'; debug(`[SHELL_ENV] Windows command: ${command}`); } else if (isWSL()) { debug("[SHELL_ENV] Windows WSL platform, using non-interactive shell");