diff --git a/images/windows/scripts/build/Configure-System.ps1 b/images/windows/scripts/build/Configure-System.ps1 index 08d09f143..8553ba68a 100644 --- a/images/windows/scripts/build/Configure-System.ps1 +++ b/images/windows/scripts/build/Configure-System.ps1 @@ -6,11 +6,30 @@ Write-Host "Cleanup WinSxS" 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 if (Test-IsWin22) { - Write-Host "Sets the default install version to v1 for new distributions" - Add-DefaultItem -DefaultVariable "DefaultVersion" -Value 1 -Name "DEFAULT\Software\Microsoft\Windows\CurrentVersion\Lxss" -Kind "DWord" + Write-Host "Setting WSL default version to 1" + + 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" @@ -70,9 +89,11 @@ $registrySettings = @( ) $registrySettings | ForEach-Object { - $regPath = $PSItem.Path - New-ItemPath -Path $regPath - New-ItemProperty @PSItem -Force -ErrorAction Ignore + $regPath = $_.Path + if (-not (Test-Path $regPath)) { + New-Item -Path $regPath -Force -ErrorAction Ignore | Out-Null + } + New-ItemProperty @_ -Force -ErrorAction Ignore } | Out-Null # Disable Template Services / User Services added by Desktop Experience @@ -86,7 +107,9 @@ $regUserServicesToDisables = @( $regUserServicesToDisables | ForEach-Object { $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 "UserServiceFlags" -Value 0 -PropertyType DWORD -Force -ErrorAction Ignore } | Out-Null diff --git a/images/windows/scripts/build/Install-Git.ps1 b/images/windows/scripts/build/Install-Git.ps1 index f7aaa256c..cc97e54fe 100644 --- a/images/windows/scripts/build/Install-Git.ps1 +++ b/images/windows/scripts/build/Install-Git.ps1 @@ -3,7 +3,6 @@ ## Desc: Install Git for Windows ## 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 $repoURL = "https://api.github.com/repos/git-for-windows/git/releases/latest" @@ -28,7 +27,7 @@ Install-Binary ` "/COMPONENTS=gitlfs") ` -ExpectedSHA256Sum $externalHash -Update-SessionEnvironment +Update-Environment git config --system --add safe.directory "*" diff --git a/images/windows/scripts/build/Install-Haskell.ps1 b/images/windows/scripts/build/Install-Haskell.ps1 index 8fee875bc..002644616 100644 --- a/images/windows/scripts/build/Install-Haskell.ps1 +++ b/images/windows/scripts/build/Install-Haskell.ps1 @@ -2,7 +2,6 @@ ## File: Install-Haskell.ps1 ## Desc: Install Haskell for Windows ################################################################################ -Import-Module "$env:ChocolateyInstall\helpers\chocolateyProfile.psm1" # install minimal ghcup, utilizing pre-installed msys2 at C:\msys64 Write-Host 'Installing ghcup...' @@ -28,8 +27,7 @@ Start-DownloadWithRetry -Url $ghcupDownloadURL -Name "ghcup.exe" -DownloadPath " [System.Environment]::SetEnvironmentVariable("CABAL_DIR", $cabalDir, "Machine") Add-MachinePathItem "$ghcupPrefix\ghcup\bin" Add-MachinePathItem "$cabalDir\bin" - -Update-SessionEnvironment +Update-Environment # Get 3 latest versions of GHC $Versions = ghcup list -t ghc -r | Where-Object {$_ -notlike "prerelease"} diff --git a/images/windows/scripts/build/Install-JavaTools.ps1 b/images/windows/scripts/build/Install-JavaTools.ps1 index efbb8a897..1b9e34d70 100644 --- a/images/windows/scripts/build/Install-JavaTools.ps1 +++ b/images/windows/scripts/build/Install-JavaTools.ps1 @@ -24,7 +24,7 @@ function Set-JavaPath { if ($Default) { # 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(';') $newPathSegments = @() @@ -39,7 +39,7 @@ function Set-JavaPath { $newPath = $javaPath + '\bin;' + $newPath 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" [Environment]::SetEnvironmentVariable("JAVA_HOME", $javaPath, "Machine") @@ -123,15 +123,14 @@ Install-ChocoPackage maven -ArgumentList "--version=$versionToInstall" Install-ChocoPackage gradle # Add maven env variables to Machine -[string]$m2 = (Get-MachinePath).Split(";") -match "maven" -$maven_opts = '-Xms256m' +[string]$m2 = ([Environment]::GetEnvironmentVariable("PATH", "Machine")).Split(";") -match "maven" $m2_repo = 'C:\ProgramData\m2' New-Item -Path $m2_repo -ItemType Directory -Force | Out-Null [Environment]::SetEnvironmentVariable("M2", $m2, "Machine") [Environment]::SetEnvironmentVariable("M2_REPO", $m2_repo, "Machine") -[Environment]::SetEnvironmentVariable("MAVEN_OPTS", $maven_opts, "Machine") +[Environment]::SetEnvironmentVariable("MAVEN_OPTS", "-Xms256m", "Machine") # Download cobertura jars $uri = 'https://repo1.maven.org/maven2/net/sourceforge/cobertura/cobertura/2.1.1/cobertura-2.1.1-bin.zip' diff --git a/images/windows/scripts/build/Install-Mercurial.ps1 b/images/windows/scripts/build/Install-Mercurial.ps1 index 96ba27ee6..8ab9382cf 100644 --- a/images/windows/scripts/build/Install-Mercurial.ps1 +++ b/images/windows/scripts/build/Install-Mercurial.ps1 @@ -5,8 +5,7 @@ Install-ChocoPackage hg -ArgumentList "--version", "5.0.0" -$hgPath = "${env:ProgramFiles}\Mercurial\" -Add-MachinePathItem $hgPath -$env:Path = Get-MachinePath +Add-MachinePathItem "${env:ProgramFiles}\Mercurial\" +Update-Environment Invoke-PesterTests -TestFile "Tools" -TestName "Mercurial" diff --git a/images/windows/scripts/build/Install-NSIS.ps1 b/images/windows/scripts/build/Install-NSIS.ps1 index 423eae9da..745c00042 100644 --- a/images/windows/scripts/build/Install-NSIS.ps1 +++ b/images/windows/scripts/build/Install-NSIS.ps1 @@ -8,8 +8,7 @@ $NsisVersion = (Get-ToolsetContent).nsis.version Install-ChocoPackage nsis -ArgumentList "--version", "$NsisVersion" -$NsisPath = "${env:ProgramFiles(x86)}\NSIS\" -Add-MachinePathItem $NsisPath -$env:Path = Get-MachinePath +Add-MachinePathItem "${env:ProgramFiles(x86)}\NSIS\" +Update-Environment Invoke-PesterTests -TestFile "Tools" -TestName "NSIS" diff --git a/images/windows/scripts/build/Install-NodeJS.ps1 b/images/windows/scripts/build/Install-NodeJS.ps1 index 0f0d00a54..f32b425ec 100644 --- a/images/windows/scripts/build/Install-NodeJS.ps1 +++ b/images/windows/scripts/build/Install-NodeJS.ps1 @@ -16,7 +16,7 @@ $versionToInstall = Resolve-ChocoPackageVersion -PackageName "nodejs" -TargetVer Install-ChocoPackage nodejs -ArgumentList "--version=$versionToInstall" Add-MachinePathItem $PrefixPath -$env:Path = Get-MachinePath +Update-Environment [Environment]::SetEnvironmentVariable("npm_config_prefix", $PrefixPath, "Machine") $env:npm_config_prefix = $PrefixPath diff --git a/images/windows/scripts/build/Install-OpenSSL.ps1 b/images/windows/scripts/build/Install-OpenSSL.ps1 index 06f5e1840..c229b3754 100644 --- a/images/windows/scripts/build/Install-OpenSSL.ps1 +++ b/images/windows/scripts/build/Install-OpenSSL.ps1 @@ -39,6 +39,6 @@ Install-Binary ` # Update PATH Add-MachinePathItem "$installDir\bin" -$env:Path = Get-MachinePath +Update-Environment Invoke-PesterTests -TestFile "Tools" -TestName "OpenSSL" diff --git a/images/windows/scripts/build/Install-Vcpkg.ps1 b/images/windows/scripts/build/Install-Vcpkg.ps1 index ef93073ac..0c138a3bd 100644 --- a/images/windows/scripts/build/Install-Vcpkg.ps1 +++ b/images/windows/scripts/build/Install-Vcpkg.ps1 @@ -15,7 +15,7 @@ Invoke-Expression "$InstallDir\$VcpkgExecPath integrate install" # Add vcpkg to system environment Add-MachinePathItem $InstallDir -$env:Path = Get-MachinePath [Environment]::SetEnvironmentVariable("VCPKG_INSTALLATION_ROOT", $InstallDir, "Machine") +Update-Environment Invoke-PesterTests -TestFile "Tools" -TestName "Vcpkg" diff --git a/images/windows/scripts/helpers/ImageHelpers.psm1 b/images/windows/scripts/helpers/ImageHelpers.psm1 index d8d04b9ac..c3bee64fa 100644 --- a/images/windows/scripts/helpers/ImageHelpers.psm1 +++ b/images/windows/scripts/helpers/ImageHelpers.psm1 @@ -7,19 +7,10 @@ param() . $PSScriptRoot\VisualStudioHelpers.ps1 Export-ModuleMember -Function @( - 'Connect-Hive' - 'Disconnect-Hive' - 'Test-MachinePath' - 'Get-MachinePath' - 'Get-DefaultPath' - 'Set-MachinePath' - 'Set-DefaultPath' + 'Mount-RegistryHive' + 'Dismount-RegistryHive' 'Add-MachinePathItem' 'Add-DefaultPathItem' - 'Add-DefaultItem' - 'Get-SystemVariable' - 'Get-DefaultVariable' - 'Set-DefaultVariable' 'Install-Binary' 'Install-VisualStudio' 'Get-ToolsetContent' @@ -47,8 +38,8 @@ Export-ModuleMember -Function @( 'Get-VisualStudioInstance' 'Get-VisualStudioComponents' 'Get-WindowsUpdateStates' - 'New-ItemPath' 'Use-ChecksumComparison' 'Get-HashFromGitHubReleaseBody' 'Test-FileSignature' + 'Update-Environment' ) diff --git a/images/windows/scripts/helpers/InstallHelpers.ps1 b/images/windows/scripts/helpers/InstallHelpers.ps1 index 036866c27..a658d0bd3 100644 --- a/images/windows/scripts/helpers/InstallHelpers.ps1 +++ b/images/windows/scripts/helpers/InstallHelpers.ps1 @@ -869,3 +869,39 @@ function Test-FileSignature { 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 + } + } + } +} diff --git a/images/windows/scripts/helpers/PathHelpers.ps1 b/images/windows/scripts/helpers/PathHelpers.ps1 index ca3a004ac..192c8b978 100644 --- a/images/windows/scripts/helpers/PathHelpers.ps1 +++ b/images/windows/scripts/helpers/PathHelpers.ps1 @@ -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( - [string]$FileName = "C:\Users\Default\NTUSER.DAT", - [string]$SubKey = "HKLM\DEFAULT" + [Parameter(Mandatory = $true)] + [string]$FileName, + [Parameter(Mandatory = $true)] + [string]$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 } $result = reg load $SubKey $FileName *>&1 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 } } -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( - [string]$SubKey = "HKLM\DEFAULT" + [Parameter(Mandatory = $true)] + [string]$SubKey ) Write-Host "Unloading the hive $SubKey" - if (-not (Test-Path $SubKey.Replace("\",":"))) { + if (-not (Test-Path $SubKey.Replace("\", ":"))) { 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 { + <# + .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( + [Parameter(Mandatory = $true)] [string]$PathItem ) - $currentPath = Get-MachinePath + $currentPath = [System.Environment]::GetEnvironmentVariable("PATH", "Machine") $newPath = $PathItem + ';' + $currentPath - Set-MachinePath -NewPath $newPath + [System.Environment]::SetEnvironmentVariable("PATH", $newPath, "Machine") } 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( + [Parameter(Mandatory = $true)] [string]$PathItem ) - Connect-Hive - $currentPath = Get-DefaultPath - $newPath = $PathItem + ';' + $currentPath - Set-DefaultPath -NewPath $newPath - Disconnect-Hive -} - -function Add-DefaultItem { - param( - [string]$DefaultVariable, - [string]$Value, - [string]$Name = "DEFAULT\Environment", - [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 - } + Mount-RegistryHive ` + -FileName "C:\Users\Default\NTUSER.DAT" ` + -SubKey "HKLM\DEFAULT" + + $key = [Microsoft.Win32.Registry]::LocalMachine.OpenSubKey("DEFAULT\Environment", $true) + $currentValue = $key.GetValue("Path", "", "DoNotExpandEnvironmentNames") + $updatedValue = $PathItem + ';' + $currentValue + $key.SetValue("Path", $updatedValue, "ExpandString") + $key.Handle.Close() + [System.GC]::Collect() + + Dismount-RegistryHive "HKLM\DEFAULT" } diff --git a/images/windows/scripts/helpers/test/PathHelpers.Tests.ps1 b/images/windows/scripts/helpers/test/PathHelpers.Tests.ps1 deleted file mode 100644 index bdbbb2aa0..000000000 --- a/images/windows/scripts/helpers/test/PathHelpers.Tests.ps1 +++ /dev/null @@ -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' - } -} diff --git a/images/windows/scripts/tests/Helpers.psm1 b/images/windows/scripts/tests/Helpers.psm1 index 0ec1e6ad8..0f00198d8 100644 --- a/images/windows/scripts/tests/Helpers.psm1 +++ b/images/windows/scripts/tests/Helpers.psm1 @@ -6,18 +6,6 @@ function Get-EnvironmentVariable($variable) { 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 { <# .SYNOPSIS