diff --git a/apps/studio/package.json b/apps/studio/package.json index 9299e1b3e2..0d1e84dccf 100644 --- a/apps/studio/package.json +++ b/apps/studio/package.json @@ -52,7 +52,6 @@ "strip-ansi": "^7.1.2", "tar": "^7.5.16", "tus-js-client": "^4.3.1", - "winreg": "1.2.4", "yauzl": "^3.2.1", "zod": "^4.0.0" }, @@ -81,7 +80,6 @@ "@types/react-dom": "^19.2.3", "@types/semver": "^7.7.1", "@types/shell-quote": "^1.7.5", - "@types/winreg": "^1.2.36", "@types/yauzl": "^2.10.3", "@vitejs/plugin-react": "^6.0.2", "@wordpress/components": "34.0.0", diff --git a/apps/studio/src/modules/cli/lib/windows-installation-manager.ts b/apps/studio/src/modules/cli/lib/windows-installation-manager.ts index e140cc8f18..b1fb02a627 100644 --- a/apps/studio/src/modules/cli/lib/windows-installation-manager.ts +++ b/apps/studio/src/modules/cli/lib/windows-installation-manager.ts @@ -1,10 +1,10 @@ import { app, dialog } from 'electron'; import { mkdir, rm, writeFile } from 'fs/promises'; +import { spawn } from 'node:child_process'; import { existsSync } from 'node:fs'; import path from 'path'; import * as Sentry from '@sentry/electron/main'; import { __ } from '@wordpress/i18n'; -import Registry from 'winreg'; // don't update winreg to 1.2.5 - https://github.com/fresc81/node-winreg/issues/65 import { getMainWindow } from 'src/main-window'; import { StudioCliInstallationManager } from 'src/modules/cli/lib/ipc-handlers'; import { loadUserData, updateAppdata } from 'src/storage/user-data'; @@ -12,13 +12,6 @@ import { loadUserData, updateAppdata } from 'src/storage/user-data'; // `STABLE_BIN_DIR_PATH` resolves to C:\Users\\AppData\Local\studio\bin export const STABLE_BIN_DIR_PATH = path.resolve( path.dirname( app.getPath( 'exe' ) ), '../bin' ); -const PATH_KEY = 'Path'; - -const currentUserRegistry = new Registry( { - hive: Registry.HKCU, - key: '\\Environment', -} ); - export class WindowsCliInstallationManager implements StudioCliInstallationManager { constructor() { if ( process.platform !== 'win32' ) { @@ -118,28 +111,46 @@ export class WindowsCliInstallationManager implements StudioCliInstallationManag } } - private getPathFromRegistry(): Promise< string > { + private runPowerShell( script: string ): Promise< string > { return new Promise( ( resolve, reject ) => { - currentUserRegistry.get( PATH_KEY, ( error, item ) => { - if ( error ) { - return reject( error ); - } + const child = spawn( + 'powershell.exe', + [ '-NoProfile', '-NonInteractive', '-WindowStyle', 'Hidden', '-Command', script ], + { windowsHide: true } + ); - resolve( item?.value || '' ); + let stdout = ''; + child.stdout.on( 'data', ( chunk ) => { + stdout += chunk.toString(); } ); + child.on( 'error', reject ); + child.on( 'exit', ( code ) => + code === 0 + ? resolve( stdout ) + : reject( new Error( `PowerShell exited with code ${ code }` ) ) + ); } ); } - private setPathInRegistry( updatedPath: string ): Promise< void > { - return new Promise( ( resolve, reject ) => { - currentUserRegistry.set( PATH_KEY, Registry.REG_EXPAND_SZ, updatedPath, ( error ) => { - if ( error ) { - return reject( error ); - } + private async getPathFromRegistry(): Promise< string > { + // Read the raw, unexpanded value (DoNotExpandEnvironmentNames) so that any + // %VAR% entries survive a read/modify/write round-trip untouched. + const script = ` +$key = [Microsoft.Win32.Registry]::CurrentUser.OpenSubKey('Environment') +if ($key) { + [Console]::Out.Write([string]$key.GetValue('Path', '', [Microsoft.Win32.RegistryValueOptions]::DoNotExpandEnvironmentNames)) + $key.Close() +}`; + return ( await this.runPowerShell( script ) ).trim(); + } - resolve(); - } ); - } ); + private async setPathInRegistry( updatedPath: string ): Promise< void > { + // SetEnvironmentVariable(..., 'User') also broadcasts WM_SETTINGCHANGE so + // open shells pick up the new PATH without a re-login. + const escaped = updatedPath.replace( /'/g, "''" ); + await this.runPowerShell( + `[Environment]::SetEnvironmentVariable('PATH', '${ escaped }', 'User')` + ); } private async isStudioCliDirInPath(): Promise< boolean > { diff --git a/package-lock.json b/package-lock.json index 8666bca93d..80ffac126c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -543,7 +543,6 @@ "strip-ansi": "^7.1.2", "tar": "^7.5.16", "tus-js-client": "^4.3.1", - "winreg": "1.2.4", "yauzl": "^3.2.1", "zod": "^4.0.0" }, @@ -572,7 +571,6 @@ "@types/react-dom": "^19.2.3", "@types/semver": "^7.7.1", "@types/shell-quote": "^1.7.5", - "@types/winreg": "^1.2.36", "@types/yauzl": "^2.10.3", "@vitejs/plugin-react": "^6.0.2", "@wordpress/components": "34.0.0", @@ -4979,9 +4977,6 @@ "cpu": [ "arm64" ], - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -4998,9 +4993,6 @@ "cpu": [ "arm64" ], - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -5017,9 +5009,6 @@ "cpu": [ "riscv64" ], - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -5036,9 +5025,6 @@ "cpu": [ "x64" ], - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -5055,9 +5041,6 @@ "cpu": [ "x64" ], - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -13813,11 +13796,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@types/winreg": { - "version": "1.2.36", - "dev": true, - "license": "MIT" - }, "node_modules/@types/wrap-ansi": { "version": "3.0.0", "dev": true, @@ -32575,10 +32553,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/winreg": { - "version": "1.2.4", - "license": "BSD-2-Clause" - }, "node_modules/word-wrap": { "version": "1.2.5", "license": "MIT",