[Windows] Refactor PATH helpers (#8885)

This commit is contained in:
Vasilii Polikarpov
2023-11-29 13:00:16 +01:00
committed by GitHub
parent c73276d3f6
commit bfe32a2b12
14 changed files with 179 additions and 191 deletions

View File

@@ -6,11 +6,30 @@
Write-Host "Cleanup WinSxS" Write-Host "Cleanup WinSxS"
Dism.exe /online /Cleanup-Image /StartComponentCleanup /ResetBase Dism.exe /online /Cleanup-Image /StartComponentCleanup /ResetBase
# Sets the default install version to v1 for new distributions # Set default version to 1 for WSL (aka LXSS - Linux Subsystem)
# The value should be set in the default user registry hive
# https://github.com/actions/runner-images/issues/5760 # https://github.com/actions/runner-images/issues/5760
if (Test-IsWin22) { if (Test-IsWin22) {
Write-Host "Sets the default install version to v1 for new distributions" Write-Host "Setting WSL default version to 1"
Add-DefaultItem -DefaultVariable "DefaultVersion" -Value 1 -Name "DEFAULT\Software\Microsoft\Windows\CurrentVersion\Lxss" -Kind "DWord"
Mount-RegistryHive `
-FileName "C:\Users\Default\NTUSER.DAT" `
-SubKey "HKLM\DEFAULT"
# Create the key if it doesn't exist
$keyPath = "DEFAULT\Software\Microsoft\Windows\CurrentVersion\Lxss"
if (-not (Test-Path $keyPath)) {
Write-Host "Creating $keyPath key"
New-Item -Path (Join-Path "HKLM:\" $keyPath) -Force | Out-Null
}
# Set the DefaultVersion value to 1
$key = [Microsoft.Win32.Registry]::LocalMachine.OpenSubKey($keyPath, $true)
$key.SetValue("DefaultVersion", "1", "DWord")
$key.Handle.Close()
[System.GC]::Collect()
Dismount-RegistryHive "HKLM\DEFAULT"
} }
Write-Host "Clean up various directories" Write-Host "Clean up various directories"
@@ -70,9 +89,11 @@ $registrySettings = @(
) )
$registrySettings | ForEach-Object { $registrySettings | ForEach-Object {
$regPath = $PSItem.Path $regPath = $_.Path
New-ItemPath -Path $regPath if (-not (Test-Path $regPath)) {
New-ItemProperty @PSItem -Force -ErrorAction Ignore New-Item -Path $regPath -Force -ErrorAction Ignore | Out-Null
}
New-ItemProperty @_ -Force -ErrorAction Ignore
} | Out-Null } | Out-Null
# Disable Template Services / User Services added by Desktop Experience # Disable Template Services / User Services added by Desktop Experience
@@ -86,7 +107,9 @@ $regUserServicesToDisables = @(
$regUserServicesToDisables | ForEach-Object { $regUserServicesToDisables | ForEach-Object {
$regPath = $_ $regPath = $_
New-ItemPath -Path $regPath if (-not (Test-Path $regPath)) {
New-Item -Path $regPath -Force -ErrorAction Ignore | Out-Null
}
New-ItemProperty -Path $regPath -Name "Start" -Value 4 -PropertyType DWORD -Force -ErrorAction Ignore New-ItemProperty -Path $regPath -Name "Start" -Value 4 -PropertyType DWORD -Force -ErrorAction Ignore
New-ItemProperty -Path $regPath -Name "UserServiceFlags" -Value 0 -PropertyType DWORD -Force -ErrorAction Ignore New-ItemProperty -Path $regPath -Name "UserServiceFlags" -Value 0 -PropertyType DWORD -Force -ErrorAction Ignore
} | Out-Null } | Out-Null

View File

@@ -3,7 +3,6 @@
## Desc: Install Git for Windows ## Desc: Install Git for Windows
## Supply chain security: Git - checksum validation, Hub CLI - managed by package manager ## Supply chain security: Git - checksum validation, Hub CLI - managed by package manager
################################################################################ ################################################################################
Import-Module "$env:ChocolateyInstall\helpers\chocolateyProfile.psm1"
# Install the latest version of Git for Windows # Install the latest version of Git for Windows
$repoURL = "https://api.github.com/repos/git-for-windows/git/releases/latest" $repoURL = "https://api.github.com/repos/git-for-windows/git/releases/latest"
@@ -28,7 +27,7 @@ Install-Binary `
"/COMPONENTS=gitlfs") ` "/COMPONENTS=gitlfs") `
-ExpectedSHA256Sum $externalHash -ExpectedSHA256Sum $externalHash
Update-SessionEnvironment Update-Environment
git config --system --add safe.directory "*" git config --system --add safe.directory "*"

View File

@@ -2,7 +2,6 @@
## File: Install-Haskell.ps1 ## File: Install-Haskell.ps1
## Desc: Install Haskell for Windows ## Desc: Install Haskell for Windows
################################################################################ ################################################################################
Import-Module "$env:ChocolateyInstall\helpers\chocolateyProfile.psm1"
# install minimal ghcup, utilizing pre-installed msys2 at C:\msys64 # install minimal ghcup, utilizing pre-installed msys2 at C:\msys64
Write-Host 'Installing ghcup...' Write-Host 'Installing ghcup...'
@@ -28,8 +27,7 @@ Start-DownloadWithRetry -Url $ghcupDownloadURL -Name "ghcup.exe" -DownloadPath "
[System.Environment]::SetEnvironmentVariable("CABAL_DIR", $cabalDir, "Machine") [System.Environment]::SetEnvironmentVariable("CABAL_DIR", $cabalDir, "Machine")
Add-MachinePathItem "$ghcupPrefix\ghcup\bin" Add-MachinePathItem "$ghcupPrefix\ghcup\bin"
Add-MachinePathItem "$cabalDir\bin" Add-MachinePathItem "$cabalDir\bin"
Update-Environment
Update-SessionEnvironment
# Get 3 latest versions of GHC # Get 3 latest versions of GHC
$Versions = ghcup list -t ghc -r | Where-Object {$_ -notlike "prerelease"} $Versions = ghcup list -t ghc -r | Where-Object {$_ -notlike "prerelease"}

View File

@@ -24,7 +24,7 @@ function Set-JavaPath {
if ($Default) { if ($Default) {
# Clean up any other Java folders from PATH to make sure that they won't conflict with each other # Clean up any other Java folders from PATH to make sure that they won't conflict with each other
$currentPath = Get-MachinePath $currentPath = [System.Environment]::GetEnvironmentVariable("PATH", "Machine")
$pathSegments = $currentPath.Split(';') $pathSegments = $currentPath.Split(';')
$newPathSegments = @() $newPathSegments = @()
@@ -39,7 +39,7 @@ function Set-JavaPath {
$newPath = $javaPath + '\bin;' + $newPath $newPath = $javaPath + '\bin;' + $newPath
Write-Host "Add $javaPath\bin to PATH" Write-Host "Add $javaPath\bin to PATH"
Set-MachinePath -NewPath $newPath [System.Environment]::SetEnvironmentVariable("PATH", $newPath, "Machine")
Write-Host "Set JAVA_HOME environmental variable as $javaPath" Write-Host "Set JAVA_HOME environmental variable as $javaPath"
[Environment]::SetEnvironmentVariable("JAVA_HOME", $javaPath, "Machine") [Environment]::SetEnvironmentVariable("JAVA_HOME", $javaPath, "Machine")
@@ -123,15 +123,14 @@ Install-ChocoPackage maven -ArgumentList "--version=$versionToInstall"
Install-ChocoPackage gradle Install-ChocoPackage gradle
# Add maven env variables to Machine # Add maven env variables to Machine
[string]$m2 = (Get-MachinePath).Split(";") -match "maven" [string]$m2 = ([Environment]::GetEnvironmentVariable("PATH", "Machine")).Split(";") -match "maven"
$maven_opts = '-Xms256m'
$m2_repo = 'C:\ProgramData\m2' $m2_repo = 'C:\ProgramData\m2'
New-Item -Path $m2_repo -ItemType Directory -Force | Out-Null New-Item -Path $m2_repo -ItemType Directory -Force | Out-Null
[Environment]::SetEnvironmentVariable("M2", $m2, "Machine") [Environment]::SetEnvironmentVariable("M2", $m2, "Machine")
[Environment]::SetEnvironmentVariable("M2_REPO", $m2_repo, "Machine") [Environment]::SetEnvironmentVariable("M2_REPO", $m2_repo, "Machine")
[Environment]::SetEnvironmentVariable("MAVEN_OPTS", $maven_opts, "Machine") [Environment]::SetEnvironmentVariable("MAVEN_OPTS", "-Xms256m", "Machine")
# Download cobertura jars # Download cobertura jars
$uri = 'https://repo1.maven.org/maven2/net/sourceforge/cobertura/cobertura/2.1.1/cobertura-2.1.1-bin.zip' $uri = 'https://repo1.maven.org/maven2/net/sourceforge/cobertura/cobertura/2.1.1/cobertura-2.1.1-bin.zip'

View File

@@ -5,8 +5,7 @@
Install-ChocoPackage hg -ArgumentList "--version", "5.0.0" Install-ChocoPackage hg -ArgumentList "--version", "5.0.0"
$hgPath = "${env:ProgramFiles}\Mercurial\" Add-MachinePathItem "${env:ProgramFiles}\Mercurial\"
Add-MachinePathItem $hgPath Update-Environment
$env:Path = Get-MachinePath
Invoke-PesterTests -TestFile "Tools" -TestName "Mercurial" Invoke-PesterTests -TestFile "Tools" -TestName "Mercurial"

View File

@@ -8,8 +8,7 @@ $NsisVersion = (Get-ToolsetContent).nsis.version
Install-ChocoPackage nsis -ArgumentList "--version", "$NsisVersion" Install-ChocoPackage nsis -ArgumentList "--version", "$NsisVersion"
$NsisPath = "${env:ProgramFiles(x86)}\NSIS\" Add-MachinePathItem "${env:ProgramFiles(x86)}\NSIS\"
Add-MachinePathItem $NsisPath Update-Environment
$env:Path = Get-MachinePath
Invoke-PesterTests -TestFile "Tools" -TestName "NSIS" Invoke-PesterTests -TestFile "Tools" -TestName "NSIS"

View File

@@ -16,7 +16,7 @@ $versionToInstall = Resolve-ChocoPackageVersion -PackageName "nodejs" -TargetVer
Install-ChocoPackage nodejs -ArgumentList "--version=$versionToInstall" Install-ChocoPackage nodejs -ArgumentList "--version=$versionToInstall"
Add-MachinePathItem $PrefixPath Add-MachinePathItem $PrefixPath
$env:Path = Get-MachinePath Update-Environment
[Environment]::SetEnvironmentVariable("npm_config_prefix", $PrefixPath, "Machine") [Environment]::SetEnvironmentVariable("npm_config_prefix", $PrefixPath, "Machine")
$env:npm_config_prefix = $PrefixPath $env:npm_config_prefix = $PrefixPath

View File

@@ -39,6 +39,6 @@ Install-Binary `
# Update PATH # Update PATH
Add-MachinePathItem "$installDir\bin" Add-MachinePathItem "$installDir\bin"
$env:Path = Get-MachinePath Update-Environment
Invoke-PesterTests -TestFile "Tools" -TestName "OpenSSL" Invoke-PesterTests -TestFile "Tools" -TestName "OpenSSL"

View File

@@ -15,7 +15,7 @@ Invoke-Expression "$InstallDir\$VcpkgExecPath integrate install"
# Add vcpkg to system environment # Add vcpkg to system environment
Add-MachinePathItem $InstallDir Add-MachinePathItem $InstallDir
$env:Path = Get-MachinePath
[Environment]::SetEnvironmentVariable("VCPKG_INSTALLATION_ROOT", $InstallDir, "Machine") [Environment]::SetEnvironmentVariable("VCPKG_INSTALLATION_ROOT", $InstallDir, "Machine")
Update-Environment
Invoke-PesterTests -TestFile "Tools" -TestName "Vcpkg" Invoke-PesterTests -TestFile "Tools" -TestName "Vcpkg"

View File

@@ -7,19 +7,10 @@ param()
. $PSScriptRoot\VisualStudioHelpers.ps1 . $PSScriptRoot\VisualStudioHelpers.ps1
Export-ModuleMember -Function @( Export-ModuleMember -Function @(
'Connect-Hive' 'Mount-RegistryHive'
'Disconnect-Hive' 'Dismount-RegistryHive'
'Test-MachinePath'
'Get-MachinePath'
'Get-DefaultPath'
'Set-MachinePath'
'Set-DefaultPath'
'Add-MachinePathItem' 'Add-MachinePathItem'
'Add-DefaultPathItem' 'Add-DefaultPathItem'
'Add-DefaultItem'
'Get-SystemVariable'
'Get-DefaultVariable'
'Set-DefaultVariable'
'Install-Binary' 'Install-Binary'
'Install-VisualStudio' 'Install-VisualStudio'
'Get-ToolsetContent' 'Get-ToolsetContent'
@@ -47,8 +38,8 @@ Export-ModuleMember -Function @(
'Get-VisualStudioInstance' 'Get-VisualStudioInstance'
'Get-VisualStudioComponents' 'Get-VisualStudioComponents'
'Get-WindowsUpdateStates' 'Get-WindowsUpdateStates'
'New-ItemPath'
'Use-ChecksumComparison' 'Use-ChecksumComparison'
'Get-HashFromGitHubReleaseBody' 'Get-HashFromGitHubReleaseBody'
'Test-FileSignature' 'Test-FileSignature'
'Update-Environment'
) )

View File

@@ -869,3 +869,39 @@ function Test-FileSignature {
throw "Signature thumbprint do not match expected." throw "Signature thumbprint do not match expected."
} }
} }
function Update-Environment {
<#
.SYNOPSIS
Updates the environment variables by reading values from the registry.
.DESCRIPTION
This function updates current environment by reading values from the registry.
It is useful when you need to update the environment variables without restarting the current session.
.NOTES
The function requires administrative privileges to modify the system registry.
#>
$locations = @(
'HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager\Environment',
'HKCU:\Environment'
)
# Update PATH variable
$pathItems = $locations | ForEach-Object {
(Get-Item $_).GetValue('PATH').Split(';')
} | Select-Object -Unique
$Env:PATH = $pathItems -join ';'
# Update other variables
$locations | ForEach-Object {
$key = Get-Item $_
foreach ($name in $key.GetValueNames()) {
$value = $key.GetValue($name)
if (-not ($name -ieq 'PATH')) {
Set-Item -Path Env:$name -Value $value
}
}
}
}

View File

@@ -1,28 +1,62 @@
function Connect-Hive {
function Mount-RegistryHive {
<#
.SYNOPSIS
Mounts a registry hive from a file.
.DESCRIPTION
The Mount-RegistryHive function loads a registry hive from a specified file into a specified subkey.
.PARAMETER FileName
The path to the file from which to load the registry hive.
.PARAMETER SubKey
The registry subkey into which to load the hive.
.EXAMPLE
Mount-RegistryHive -FileName "C:\Path\To\HiveFile.hiv" -SubKey "HKLM\SubKey"
#>
param( param(
[string]$FileName = "C:\Users\Default\NTUSER.DAT", [Parameter(Mandatory = $true)]
[string]$SubKey = "HKLM\DEFAULT" [string]$FileName,
[Parameter(Mandatory = $true)]
[string]$SubKey
) )
Write-Host "Loading the file $FileName to the Key $SubKey" Write-Host "Loading the file $FileName to the Key $SubKey"
if (Test-Path $SubKey.Replace("\",":")) { if (Test-Path $SubKey.Replace("\", ":")) {
Write-Warning "The key $SubKey is already loaded"
return return
} }
$result = reg load $SubKey $FileName *>&1 $result = reg load $SubKey $FileName *>&1
if ($LASTEXITCODE -ne 0) { if ($LASTEXITCODE -ne 0) {
Write-Host "Failed to load hive: $result" Write-Error "Failed to load file $FileName to the key ${SubKey}: $result"
exit 1 exit 1
} }
} }
function Disconnect-Hive { function Dismount-RegistryHive {
<#
.SYNOPSIS
Dismounts a registry hive.
.DESCRIPTION
The Dismount-RegistryHive function unloads a registry hive from a specified subkey.
.PARAMETER SubKey
The registry subkey from which to unload the hive.
.EXAMPLE
Dismount-RegistryHive -SubKey "HKLM\SubKey"
#>
param( param(
[string]$SubKey = "HKLM\DEFAULT" [Parameter(Mandatory = $true)]
[string]$SubKey
) )
Write-Host "Unloading the hive $SubKey" Write-Host "Unloading the hive $SubKey"
if (-not (Test-Path $SubKey.Replace("\",":"))) { if (-not (Test-Path $SubKey.Replace("\", ":"))) {
return return
} }
@@ -33,123 +67,72 @@ function Disconnect-Hive {
} }
} }
function Get-SystemVariable {
param(
[string]$SystemVariable
)
[System.Environment]::GetEnvironmentVariable($SystemVariable, "Machine")
}
function Get-DefaultVariable {
param(
[string]$DefaultVariable,
[string]$Name = "DEFAULT\Environment",
[bool]$Writable = $false
)
$key = [Microsoft.Win32.Registry]::LocalMachine.OpenSubKey($Name, $Writable)
$key.GetValue($DefaultVariable, "", "DoNotExpandEnvironmentNames")
$key.Handle.Close()
[System.GC]::Collect()
}
function Set-DefaultVariable {
param(
[string]$DefaultVariable,
[string]$Value,
[string]$Name = "DEFAULT\Environment",
[string]$Kind = "ExpandString",
[bool]$Writable = $true
)
$key = [Microsoft.Win32.Registry]::LocalMachine.OpenSubKey($Name, $Writable)
$key.SetValue($DefaultVariable, $Value, $Kind)
Get-DefaultVariable -DefaultVariable $DefaultVariable -Name $Name
$key.Handle.Close()
[System.GC]::Collect()
}
function Get-MachinePath {
Get-SystemVariable PATH
}
function Get-DefaultPath {
Get-DefaultVariable Path
}
function Set-MachinePath {
param(
[string]$NewPath
)
[System.Environment]::SetEnvironmentVariable("PATH", $NewPath, "Machine")
}
function Set-DefaultPath {
param(
[string]$NewPath
)
Set-DefaultVariable PATH $NewPath
}
function Test-MachinePath {
param(
[string]$PathItem
)
$pathItems = (Get-MachinePath).Split(';')
$pathItems.Contains($PathItem)
}
function Add-MachinePathItem { function Add-MachinePathItem {
<#
.SYNOPSIS
Adds a new item to the machine-level PATH environment variable.
.DESCRIPTION
The Add-MachinePathItem function adds a new item to the machine-level PATH environment variable.
It takes a string parameter, $PathItem, which represents the new item to be added to the PATH.
.PARAMETER PathItem
Specifies the new item to be added to the machine-level PATH environment variable.
.EXAMPLE
Add-MachinePathItem -PathItem "C:\Program Files\MyApp"
This example adds "C:\Program Files\MyApp" to the machine-level PATH environment variable.
#>
param( param(
[Parameter(Mandatory = $true)]
[string]$PathItem [string]$PathItem
) )
$currentPath = Get-MachinePath $currentPath = [System.Environment]::GetEnvironmentVariable("PATH", "Machine")
$newPath = $PathItem + ';' + $currentPath $newPath = $PathItem + ';' + $currentPath
Set-MachinePath -NewPath $newPath [System.Environment]::SetEnvironmentVariable("PATH", $newPath, "Machine")
} }
function Add-DefaultPathItem { function Add-DefaultPathItem {
<#
.SYNOPSIS
Adds a path item to the default user profile path.
.DESCRIPTION
This function adds a specified path item to the default user profile path.
It mounts the NTUSER.DAT file of the default user to the registry,
retrieves the current value of the "Path" environment variable,
appends the new path item to it, and updates the registry with the modified value.
.PARAMETER PathItem
The path item to be added to the default user profile path.
.EXAMPLE
Add-DefaultPathItem -PathItem "C:\Program Files\MyApp"
This example adds "C:\Program Files\MyApp" to the default user profile path.
.NOTES
This function requires administrative privileges to modify the Windows registry.
#>
param( param(
[Parameter(Mandatory = $true)]
[string]$PathItem [string]$PathItem
) )
Connect-Hive Mount-RegistryHive `
$currentPath = Get-DefaultPath -FileName "C:\Users\Default\NTUSER.DAT" `
$newPath = $PathItem + ';' + $currentPath -SubKey "HKLM\DEFAULT"
Set-DefaultPath -NewPath $newPath
Disconnect-Hive $key = [Microsoft.Win32.Registry]::LocalMachine.OpenSubKey("DEFAULT\Environment", $true)
} $currentValue = $key.GetValue("Path", "", "DoNotExpandEnvironmentNames")
$updatedValue = $PathItem + ';' + $currentValue
function Add-DefaultItem { $key.SetValue("Path", $updatedValue, "ExpandString")
param( $key.Handle.Close()
[string]$DefaultVariable, [System.GC]::Collect()
[string]$Value,
[string]$Name = "DEFAULT\Environment", Dismount-RegistryHive "HKLM\DEFAULT"
[string]$Kind = "ExpandString",
[bool]$Writable = $true
)
Connect-Hive
$regPath = Join-Path "HKLM:\" $Name
if (-not (Test-Path $Name)) {
Write-Host "Creating $regPath key"
New-Item -Path $regPath -Force | Out-Null
}
Set-DefaultVariable -DefaultVariable $DefaultVariable -Value $Value -Name $Name -Kind $Kind -Writable $Writable
Disconnect-Hive
}
function New-ItemPath {
param (
[string]$Path
)
if (-not (Test-Path $Path)) {
New-Item -Path $Path -Force -ErrorAction Ignore | Out-Null
}
} }

View File

@@ -1,27 +0,0 @@
. $PSScriptRoot\..\PathHelpers.ps1
Describe 'Test-MachinePath Tests' {
Mock Get-MachinePath {return "C:\foo;C:\bar"}
It 'Path contains item' {
Test-MachinePath -PathItem "C:\foo" | Should Be $true
}
It 'Path does not containe item' {
Test-MachinePath -PathItem "C:\baz" | Should Be $false
}
}
Describe 'Set-MachinePath Tests' {
Mock Get-MachinePath {return "C:\foo;C:\bar"}
Mock Set-ItemProperty {return}
It 'Set-MachinePath should return new path' {
Set-MachinePath -NewPath "C:\baz" | Should Be "C:\baz"
}
}
Describe "Add-MachinePathItem Tests"{
Mock Get-MachinePath {return "C:\foo;C:\bar"}
Mock Set-ItemProperty {return}
It 'Add-MachinePathItem should return complete path' {
Add-MachinePathItem -PathItem 'C:\baz' | Should Be 'C:\baz;C:\foo;C:\bar'
}
}

View File

@@ -6,18 +6,6 @@ function Get-EnvironmentVariable($variable) {
return [System.Environment]::GetEnvironmentVariable($variable, "Machine") return [System.Environment]::GetEnvironmentVariable($variable, "Machine")
} }
# Update environment variables without reboot
function Update-Environment {
$variables = [Environment]::GetEnvironmentVariables("Machine")
$variables.Keys | ForEach-Object {
$key = $_
$value = $variables[$key]
Set-Item -Path "env:$key" -Value $value
}
# We need to refresh PATH the latest one because it could include other variables "%M2_HOME%/bin"
$env:PATH = [Environment]::GetEnvironmentVariable("PATH", "Machine")
}
function Invoke-PesterTests { function Invoke-PesterTests {
<# <#
.SYNOPSIS .SYNOPSIS