From 90d71d0391de7b81a2de1b8a879a59f56133f975 Mon Sep 17 00:00:00 2001 From: confused-Techie Date: Sun, 17 Mar 2024 23:54:48 -0700 Subject: [PATCH 1/4] Remove all `Machine` handling of path modification, ensure to safely modify path --- .../settings-view/lib/system-windows-panel.js | 83 +++-------- resources/win/modifyWindowsPath.ps1 | 135 +++++++++--------- src/main-process/win-shell.js | 36 ++--- 3 files changed, 97 insertions(+), 157 deletions(-) diff --git a/packages/settings-view/lib/system-windows-panel.js b/packages/settings-view/lib/system-windows-panel.js index 439e7b589..6ba1e1123 100644 --- a/packages/settings-view/lib/system-windows-panel.js +++ b/packages/settings-view/lib/system-windows-panel.js @@ -20,14 +20,7 @@ export default class SystemPanel { WinShell.fileHandler.isRegistered((i) => { this.refs.fileHandlerCheckbox.checked = i }) WinShell.fileContextMenu.isRegistered((i) => { this.refs.fileContextMenuCheckbox.checked = i }) WinShell.folderContextMenu.isRegistered((i) => { this.refs.folderContextMenuCheckbox.checked = i }) - - if (this.isLikelyUserInstall()) { - WinShell.pathUser.isRegistered((i) => { this.refs.addToPathCheckbox.checked = i }) - } else { - WinShell.pathMachine.isRegistered((i) => { this.refs.addToPathMachineCheckbox.checked = i }) - // Check if Pulsar is running as Admin. To know if the user can modify the machine path - WinShell.runningAsAdmin((i) => { this.refs.addToPathMachineCheckbox.disabled = !i }) - } + WinShell.pathUser.isRegistered((i) => { this.refs.addToPathCheckbox.checked = i }) } destroy () { @@ -120,63 +113,29 @@ export default class SystemPanel { } } - isLikelyUserInstall() { - let resourcePath = atom.applicationDelegate.getWindowLoadSettings().resourcePath; - if (resourcePath.includes("AppData\\Local\\Programs\\pulsar")) { - return true; - } else { - return false; - } - } - getPathUI() { - if (this.isLikelyUserInstall()) { - return ( -
-
-
- -
+ return ( +
+
+
+
- ); - } else { - return ( -
-
-
- -
-
-
- ); - } +
+ ); } focus () { diff --git a/resources/win/modifyWindowsPath.ps1 b/resources/win/modifyWindowsPath.ps1 index f02be5f1e..ab7c5cd66 100644 --- a/resources/win/modifyWindowsPath.ps1 +++ b/resources/win/modifyWindowsPath.ps1 @@ -3,97 +3,94 @@ # Example Usage: # Pulsar User Installation: -# .\_.ps1 -installMode User -installdir "$INSTDIR" -remove 0 +# .\_.ps1 -installdir "$INSTDIR" -remove 0 # Pulsar Machine Installation: -# .\_.ps1 -installMode Machine -installdir "$INSTDIR" -remove 0 +# .\_.ps1 -installdir "$INSTDIR" -remove 0 # Pulsar User Uninstallation: -# .\_.ps1 -installMode User -installdir "$INSTDIR" -remove 1 +# .\_.ps1 -installdir "$INSTDIR" -remove 1 # Pulsar Machine Uninstallation: -# .\_.ps1 -installMode Machine -installdir "$INSTDIR" -remove 1 +# .\_.ps1 -installdir "$INSTDIR" -remove 1 -param ($installMode,$installdir,$remove) +# For safe interaction with environment variables taken from: +# https://github.com/chocolatey/choco/blob/HEAD/src/chocolatey.resources/helpers/functions/Set-EnvironmentVariable.ps1 +# https://github.com/chocolatey/choco/blob/HEAD/src/chocolatey.resources/helpers/functions/Get-EnvironmentVariable.ps1 +# https://github.com/chocolatey/choco/blob/HEAD/src/chocolatey.resources/helpers/functions/Install-ChocolateyPath.ps1 + +param ($installdir,$remove) # When self-elevating, we can't pass a raw boolean. Meaning we accept anything then convert $remove = [System.Convert]::ToBoolean($remove) -# Only when modifying the Machine PATH, it takes much longer than expected. So here's a loading bar -$prog = 1 -Write-Progress -Activity "Modifying Pulsar ($installdir) on the PATH..." -Status "$prog% Complete:" -PercentComplete $prog - -if ($installMode -eq "Machine") { - # PowerShell needs to be running as Admin to modify the Machine Variables - # So lets attempt to self-elevate - if (-Not ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] 'Administrator')) { - if ([int](Get-CimInstance -Class Win32_OperatingSystem | Select-Object -ExpandProperty BuildNumber) -ge 6000) { - - $processOptions = @{ - FilePath = "PowerShell.exe" - Wait = $true - PassThru = $true - Verb = "RunAs" - ArgumentList = "-File `"" + $MyInvocation.MyCommand.Path + "`" -installMode $installMode -installdir `"" + $installdir + "`" -remove $remove" - } - - Start-Process @processOptions - - Exit - } - } -} - if (-not $remove) { - if ($installMode -eq "User" -or $installMode -eq "Machine") { + # We want to add Pulsar path values - $prog = 25 - Write-Progress -Activity "Modifying Pulsar ($installdir) on the PATH..." -Status "$prog% Complete:" -PercentComplete $prog + # Lets first save a copy of the users current path + $env:Path >> prior2addition.txt; - [Environment]::SetEnvironmentVariable("Path", $env:Path + ";$installdir\resources;$installdir\resources\app\ppm\bin", $installMode) + $originalPathToInstall = $installdir - $prog = 50 - Write-Progress -Activity "Modifying Pulsar ($installdir) on the PATH..." -Status "$prog% Complete:" -PercentComplete $prog + $pulsarPath = $installdir + "\resources"; + $ppmPath = $installdir + "\resources\app\ppm\bin"; - # While this originally attempting to use the string '%USERPROFILE%' to avoid taking - # space on the PATH, whatever reads this path at startup in Pulsar, can't handle - # the variable, and instead creates the directory of the same name - # within the current folder. But only when opened via the context menu, terminal - # is fine. - $exitCode = [Environment]::SetEnvironmentVariable("ATOM_HOME", "$env:UserProfile\.pulsar", $installMode) + # Get the current PATH variable + $envPath = $env:PATH; + if (!$envPath.toLower().Contains($installdir.ToLower())) { + # we don't already have the correct environment variable + $actualPath = [Environment]::GetEnvironmentVariable("Path", [System.EnvironmentVariableTarget]::User); - $prog = 100 - Write-Progress -Activity "Modifying Pulsar ($installdir) on the PATH..." -Status "$prog% Complete:" -PercentComplete $prog + $statementTerminator = ";"; - Exit $exitCode + $pathToInstall = $pulsarPath + $statementTerminator + $ppmPath + $statementTerminator; + + # Does the path end in ';'? + $hasStatementTerminator = $actualPath -ne $null -and $actualPath.EndsWith($statementTerminator); + # If the last digit is not ';', then add it + if (!$hasStatementTerminator -and $actualPath -ne $null) { + $pathToInstall = $statementTerminator + $pathToInstall; + } + + $actualPath = $actualPath + $pathToInstall; + + # Now to actually set the path to the system + $registryType = [Microsoft.Win32.RegistryValueKind]::ExpandString; + $keyHive = "HKEY_CURRENT_USER"; + $registryKey = "Environment"; + + $exitCode = [Microsoft.Win32.Registry]::SetValue($keyHive + "\" + $registryKey, "Path", $actualPath, [System.EnvironmentVariableTarget]::User); + + Exit $exitCode; + } else { + Write-Host "Pulsar is already present on the User PATH."; } + } else { - if ($installMode -eq "User" -or $installMode -eq "Machine") { + # We want to remove Pulsar from the user path - $prog = 25 - Write-Progress -Activity "Modifying Pulsar ($installdir) on the PATH..." -Status "$prog% Complete:" -PercentComplete $prog + # Lets first save a copy of the users current path + $env:Path >> prior2removal.txt; - $path = [Environment]::GetEnvironmentVariable("Path", $installMode) + $pulsarPath = $installdir + "\resources"; + $ppmPath = $installdir + "\resources\app\ppm\bin"; - $prog = 50 - Write-Progress -Activity "Modifying Pulsar ($installdir) on the PATH..." -Status "$prog% Complete:" -PercentComplete $prog + # Get the current PATH variable + $envPath = $env:PATH; + if ($envPath.toLower().Contains($installdir.ToLower())) { + # the install dir is in fact on the path + $actualPath = [Environment]::GetEnvironmentVariable("Path", [System.EnvironmentVariableTarget]::User); - # Remove unwanted element from path - $path = ($path.Split(";") | Where-Object { $_ -ne "$installdir\resources" }) -join ";" - $path = ($path.Split(";") | Where-Object { $_ -ne "$installdir\resources\app\ppm\bin" }) -join ";" + $actualPath = ($actualPath.Split(";") | Where-Object { $_ -ne $ppmPath }) -join ";"; + # Order is important, as Pulsar's path INCLUDES ppm's path + $actualPath = ($actualPath.Split(";") | Where-Object { $_ -ne $pulsarPath }) -join ";"; - $prog = 75 - Write-Progress -Activity "Modifying Pulsar ($installdir) on the PATH..." -Status "$prog% Complete:" -PercentComplete $prog + # Now to actually set the path to the system + $registryType = [Microsoft.Win32.RegistryValueKind]::ExpandString; + $keyHive = "HKEY_CURRENT_USER"; + $registryKey = "Environment"; - # Set our new path - [Environment]::SetEnvironmentVariable("Path", $path, $installMode) + $exitCode = [Microsoft.Win32.Registry]::SetValue($keyHive + "\" + $registryKey, "Path", $actualPath, [System.EnvironmentVariableTarget]::User); - $prog = 90 - Write-Progress -Activity "Modifying Pulsar ($installdir) on the PATH..." -Status "$prog% Complete:" -PercentComplete $prog - - # Set ATOM_HOME path - $exitCode = [Environment]::SetEnvironmentVariable("ATOM_HOME", $null, $installMode) - - $prog = 100 - Write-Progress -Activity "Modifying Pulsar ($installdir) on the PATH..." -Status "$prog% Complete:" -PercentComplete $prog - - Exit $exitCode - } # Else we have been given bad params, and will silently exit + Exit $exitCode; + } else { + Write-Host "Pulsar is not present on the User PATH."; + } } diff --git a/src/main-process/win-shell.js b/src/main-process/win-shell.js index 05353d064..d3d41d2b4 100644 --- a/src/main-process/win-shell.js +++ b/src/main-process/win-shell.js @@ -74,27 +74,17 @@ class ShellOption { } class PathOption { - constructor(installType) { - // installType MUST be 'User' or 'Machine' + constructor() { this.HKPATH; this.hive; this.installReg = "\\SOFTWARE\\0949b555-c22c-56b7-873a-a960bdefa81f"; this.installMode = installType; - if (installType === "User") { - this.HKPATH = "\\Environment"; - this.hive = "HKCU"; - } else if (installType === "Machine") { - this.HKPATH = "\\SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment"; - this.hive = "HKLM"; - } + // We no longer support an `installType` + // Only managing the path of the current user + this.HKPATH = "\\Environment"; + this.hive = "HKCU"; - // Unfortunately, we can only manage the PATH for a per user installation. - // While the PowerShell script does support setting the PATH for a Machine - // install, we can't yet check that. - // https://github.com/fresc81/node-winreg/tree/1.2.1#troubleshooting - // This can only be done if Pulsar is run as Admin, with a user with Admin privs - // So we will pretend a user install is all that matters here this.isRegistered = this.isRegistered.bind(this); this.register = this.register.bind(this); this.deregister = this.deregister.bind(this); @@ -129,8 +119,8 @@ class PathOption { register(callback) { this.getPulsarPath().then((pulsarPath) => { const child = ChildProcess.execFile( - `${pulsarPath}\\resources\\modifyWindowsPath.ps1`, - ['-installMode', this.installMode, '-installdir', `"${pulsarPath}"`, '-remove', '0'], + `"${pulsarPath}\\resources\\modifyWindowsPath.ps1"`, + ['-installdir', `"${pulsarPath}"`, '-remove', '0'], { shell: "powershell.exe" }, (error, stdout, stderr) => { @@ -151,8 +141,8 @@ class PathOption { if (isRegistered) { this.getPulsarPath().then((pulsarPath) => { const child = ChildProcess.execFile( - `${pulsarPath}\\resources\\modifyWindowsPath.ps1`, - ['-installMode', this.installMode, '-installdir', `"${pulsarPath}"`, '-remove', '1'], + `"${pulsarPath}\\resources\\modifyWindowsPath.ps1"`, + ['-installdir', `"${pulsarPath}"`, '-remove', '1'], { shell: "powershell.exe" }, (error, stdout, stderr) => { @@ -188,12 +178,7 @@ class PathOption { reject("Unable to find Pulsar Install Path"); } - // When we are modifying Machine values, we can't accept spaces in the - // path. There's likely some combination of escapes to fix this, but - // I was unable to find them. For now we will check for the default - // Machine install location, and remove the space. - let safePulsarPath = pulsarPath.replace("Program Files", "PROGRA~1"); - resolve(safePulsarPath); + resolve(pulsarPath); } }); }); @@ -241,4 +226,3 @@ exports.folderBackgroundContextMenu = new ShellOption( JSON.parse(JSON.stringify(contextParts).replace('%1', '%V')) ); exports.pathUser = new PathOption("User"); -exports.pathMachine = new PathOption("Machine"); From 1e658d073539f454857ae0a36dd4ec06a62f6bc3 Mon Sep 17 00:00:00 2001 From: confused_techie Date: Mon, 25 Mar 2024 07:21:10 -0700 Subject: [PATCH 2/4] Fix un-removed variable --- src/main-process/win-shell.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main-process/win-shell.js b/src/main-process/win-shell.js index d3d41d2b4..40831c27c 100644 --- a/src/main-process/win-shell.js +++ b/src/main-process/win-shell.js @@ -78,7 +78,6 @@ class PathOption { this.HKPATH; this.hive; this.installReg = "\\SOFTWARE\\0949b555-c22c-56b7-873a-a960bdefa81f"; - this.installMode = installType; // We no longer support an `installType` // Only managing the path of the current user From eda5443bda7512c3499438b4850f9a9988ec2e12 Mon Sep 17 00:00:00 2001 From: confused-Techie Date: Mon, 25 Mar 2024 16:58:06 -0700 Subject: [PATCH 3/4] Ensure script can launch, override path values on logging, add reboot warning --- packages/settings-view/lib/system-windows-panel.js | 2 +- resources/win/modifyWindowsPath.ps1 | 4 ++-- src/main-process/win-shell.js | 10 +++++----- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/settings-view/lib/system-windows-panel.js b/packages/settings-view/lib/system-windows-panel.js index 6ba1e1123..efe42a98a 100644 --- a/packages/settings-view/lib/system-windows-panel.js +++ b/packages/settings-view/lib/system-windows-panel.js @@ -129,7 +129,7 @@ export default class SystemPanel { }} />
Add Pulsar to PATH
- Add Pulsar to Windows PATH to enable CLI usage. + Add Pulsar to Windows PATH to enable CLI usage. (May require a reboot to take effect.)
diff --git a/resources/win/modifyWindowsPath.ps1 b/resources/win/modifyWindowsPath.ps1 index ab7c5cd66..71b928c93 100644 --- a/resources/win/modifyWindowsPath.ps1 +++ b/resources/win/modifyWindowsPath.ps1 @@ -25,7 +25,7 @@ if (-not $remove) { # We want to add Pulsar path values # Lets first save a copy of the users current path - $env:Path >> prior2addition.txt; + $env:Path > prior2addition.txt; $originalPathToInstall = $installdir @@ -67,7 +67,7 @@ if (-not $remove) { # We want to remove Pulsar from the user path # Lets first save a copy of the users current path - $env:Path >> prior2removal.txt; + $env:Path > prior2removal.txt; $pulsarPath = $installdir + "\resources"; $ppmPath = $installdir + "\resources\app\ppm\bin"; diff --git a/src/main-process/win-shell.js b/src/main-process/win-shell.js index 40831c27c..e96c5a3d3 100644 --- a/src/main-process/win-shell.js +++ b/src/main-process/win-shell.js @@ -118,13 +118,13 @@ class PathOption { register(callback) { this.getPulsarPath().then((pulsarPath) => { const child = ChildProcess.execFile( - `"${pulsarPath}\\resources\\modifyWindowsPath.ps1"`, - ['-installdir', `"${pulsarPath}"`, '-remove', '0'], + `powershell.exe '${pulsarPath}\\resources\\modifyWindowsPath.ps1'`, + ['-installdir', `'${pulsarPath}'`, '-remove', '0'], { shell: "powershell.exe" }, (error, stdout, stderr) => { if (error) { - atom.notifications.addError(`Error Running Script: ${error.toString()}`); + atom.notifications.addError(`Error Running Script: ${error.toString()}`, { dismissable: true }); callback(error); } else { return callback(); @@ -140,13 +140,13 @@ class PathOption { if (isRegistered) { this.getPulsarPath().then((pulsarPath) => { const child = ChildProcess.execFile( - `"${pulsarPath}\\resources\\modifyWindowsPath.ps1"`, + `powershell.exe "${pulsarPath}\\resources\\modifyWindowsPath.ps1"`, ['-installdir', `"${pulsarPath}"`, '-remove', '1'], { shell: "powershell.exe" }, (error, stdout, stderr) => { if (error) { - atom.notifications.addError(`Error Running Script: ${error.toString()}`); + atom.notifications.addError(`Error Running Script: ${error.toString()}`, { dismissable: true }); callback(error); } else { return callback(); From 22bfa5c038f3bab6e1be2b4f65311f2175c930d4 Mon Sep 17 00:00:00 2001 From: confused-Techie Date: Tue, 26 Mar 2024 02:24:21 -0700 Subject: [PATCH 4/4] Much more logging, compatibility with Machine Installs --- resources/win/modifyWindowsPath.ps1 | 8 +++---- src/main-process/win-shell.js | 36 ++++++++++++++++++++++++----- 2 files changed, 34 insertions(+), 10 deletions(-) diff --git a/resources/win/modifyWindowsPath.ps1 b/resources/win/modifyWindowsPath.ps1 index 71b928c93..2ae10b5dd 100644 --- a/resources/win/modifyWindowsPath.ps1 +++ b/resources/win/modifyWindowsPath.ps1 @@ -3,13 +3,13 @@ # Example Usage: # Pulsar User Installation: -# .\_.ps1 -installdir "$INSTDIR" -remove 0 +# .\_.ps1 -installdir "$INSTDIR" -remove FALSE # Pulsar Machine Installation: -# .\_.ps1 -installdir "$INSTDIR" -remove 0 +# .\_.ps1 -installdir "$INSTDIR" -remove FALSE # Pulsar User Uninstallation: -# .\_.ps1 -installdir "$INSTDIR" -remove 1 +# .\_.ps1 -installdir "$INSTDIR" -remove TRUE # Pulsar Machine Uninstallation: -# .\_.ps1 -installdir "$INSTDIR" -remove 1 +# .\_.ps1 -installdir "$INSTDIR" -remove TRUE # For safe interaction with environment variables taken from: # https://github.com/chocolatey/choco/blob/HEAD/src/chocolatey.resources/helpers/functions/Set-EnvironmentVariable.ps1 diff --git a/src/main-process/win-shell.js b/src/main-process/win-shell.js index e96c5a3d3..26060cdd6 100644 --- a/src/main-process/win-shell.js +++ b/src/main-process/win-shell.js @@ -118,19 +118,24 @@ class PathOption { register(callback) { this.getPulsarPath().then((pulsarPath) => { const child = ChildProcess.execFile( - `powershell.exe '${pulsarPath}\\resources\\modifyWindowsPath.ps1'`, - ['-installdir', `'${pulsarPath}'`, '-remove', '0'], + `powershell.exe -File '${pulsarPath}\\resources\\modifyWindowsPath.ps1'`, + ['-installdir', `'${pulsarPath}'`, '-remove', 'FALSE'], { shell: "powershell.exe" }, (error, stdout, stderr) => { if (error) { + console.log(`Add Pulsar to PATH: ${error}`); atom.notifications.addError(`Error Running Script: ${error.toString()}`, { dismissable: true }); callback(error); } else { + console.log("Add Pulsar to PATH:"); + console.log(`stdout: ${stdout}`); + console.log(`stderr: ${stderr}`); return callback(); } }); }).catch((err) => { + console.error(`Add Pulsar to PATH error caught: ${err}`); return callback(err); }); } @@ -140,19 +145,24 @@ class PathOption { if (isRegistered) { this.getPulsarPath().then((pulsarPath) => { const child = ChildProcess.execFile( - `powershell.exe "${pulsarPath}\\resources\\modifyWindowsPath.ps1"`, - ['-installdir', `"${pulsarPath}"`, '-remove', '1'], + `powershell.exe -File '${pulsarPath}\\resources\\modifyWindowsPath.ps1'`, + ['-installdir', `'${pulsarPath}'`, '-remove', 'TRUE'], { shell: "powershell.exe" }, (error, stdout, stderr) => { if (error) { + console.error(`Remove Pulsar from PATH: ${error}`); atom.notifications.addError(`Error Running Script: ${error.toString()}`, { dismissable: true }); callback(error); } else { + console.log("Remove Pulsar from PATH:"); + console.log(`stdout: ${stdout}`); + console.log(`stderr: ${stderr}`); return callback(); } }); }).catch((err) => { + console.error(`Remove Pulsar from PATH error caught: ${err}`); return callback(err); }); } else { @@ -169,12 +179,26 @@ class PathOption { key: this.installReg }).get("InstallLocation", (err, val) => { if (err) { - reject(err); + console.error(err); + let location = process.resourcesPath; + if (typeof location !== "string" || location.length < 1) { + console.error(`Unable to locate Pulsar PATH via fallback methods: '${location}'`); + reject(err); + } else { + resolve(Path.dirname(location)); + } } else { pulsarPath = val.value; if (pulsarPath.length === 0) { - reject("Unable to find Pulsar Install Path"); + console.error("Unable to find Pulsar Install Path"); + let location = process.resourcesPath; + if (typeof location !== "string" || location.length < 1) { + console.error(`Unable to locate Pulsar PATH via fallback methods: '${location}'`); + reject("Unable to find Pulsar Install Path"); + } else { + resolve(Path.dirname(location)); + } } resolve(pulsarPath);