From 5ed26150179d5ccb6476e822748f284ef4a6ae7f Mon Sep 17 00:00:00 2001 From: Vasilii Polikarpov <126792224+vpolikarpov-akvelon@users.noreply.github.com> Date: Mon, 4 Dec 2023 10:50:53 +0100 Subject: [PATCH] [Windows] Cleanup various scripts (#8942) * Use Resolve-GithubReleaseAssetUrl more widely * Add the Get-ChecksumFromUrl function * Sort exported functions and add docs * Remove alias and fix typo * Fix kind checksum url and syntax * Fix checksums url for gh cli and msys2 * [Windows] Cleanup various scripts * Add spaces after type specifications * Rename the Take-Part function --- CONTRIBUTING.md | 2 +- .../scripts/build/Configure-PowerShell.ps1 | 2 +- .../scripts/build/Install-AWSTools.ps1 | 2 +- .../scripts/build/Install-AliyunCli.ps1 | 14 +- .../scripts/build/Install-AzureCli.ps1 | 18 +-- .../scripts/build/Install-AzureDevOpsCli.ps1 | 26 ++-- .../windows/scripts/build/Install-Bazel.ps1 | 3 + .../scripts/build/Install-Chocolatey.ps1 | 14 +- .../windows/scripts/build/Install-Chrome.ps1 | 17 +-- .../windows/scripts/build/Install-Docker.ps1 | 12 +- .../scripts/build/Install-DockerWinCred.ps1 | 33 ++-- .../scripts/build/Install-DotnetSDK.ps1 | 143 +++++++++--------- .../windows/scripts/build/Install-Firefox.ps1 | 16 +- images/windows/scripts/build/Install-Git.ps1 | 4 +- .../scripts/build/Install-GitHub-CLI.ps1 | 20 ++- .../windows/scripts/build/Install-Haskell.ps1 | 11 +- .../scripts/build/Install-IEWebDriver.ps1 | 2 +- .../scripts/build/Install-JavaTools.ps1 | 12 +- .../windows/scripts/build/Install-Kotlin.ps1 | 2 +- .../scripts/build/Install-KubernetesTools.ps1 | 22 +-- .../windows/scripts/build/Install-Mingw64.ps1 | 116 +++++++------- .../scripts/build/Install-Miniconda.ps1 | 2 +- .../windows/scripts/build/Install-MongoDB.ps1 | 16 +- .../windows/scripts/build/Install-Msys2.ps1 | 28 ++-- .../scripts/build/Install-MysqlCli.ps1 | 8 +- images/windows/scripts/build/Install-Pipx.ps1 | 4 +- .../scripts/build/Install-PostgreSQL.ps1 | 14 +- .../build/Install-PowerShellModules.ps1 | 14 +- .../scripts/build/Install-PowershellCore.ps1 | 11 +- .../windows/scripts/build/Install-RootCA.ps1 | 34 ++--- images/windows/scripts/build/Install-Ruby.ps1 | 102 +++++-------- .../windows/scripts/build/Install-Runner.ps1 | 9 +- images/windows/scripts/build/Install-Rust.ps1 | 2 +- .../build/Install-SQLPowerShellTools.ps1 | 12 +- .../build/Install-ServiceFabricSDK.ps1 | 10 +- .../windows/scripts/build/Install-Stack.ps1 | 20 ++- .../windows/scripts/build/Install-Toolset.ps1 | 11 +- .../scripts/build/Install-VCRedist.ps1 | 12 +- .../scripts/build/Install-VisualStudio.ps1 | 17 +-- .../build/Install-WebPlatformInstaller.ps1 | 4 +- .../scripts/build/Install-WinAppDriver.ps1 | 11 +- .../scripts/build/Install-WindowsFeatures.ps1 | 5 + .../scripts/build/Install-WindowsUpdates.ps1 | 3 +- images/windows/scripts/build/Install-Zstd.ps1 | 13 +- .../docs-gen/SoftwareReport.CachedTools.psm1 | 10 +- .../docs-gen/SoftwareReport.Common.psm1 | 87 +++++------ .../docs-gen/SoftwareReport.Helpers.psm1 | 4 +- .../scripts/docs-gen/SoftwareReport.Java.psm1 | 4 +- .../docs-gen/SoftwareReport.Tools.psm1 | 4 +- .../windows/scripts/helpers/ChocoHelpers.ps1 | 44 +++++- .../windows/scripts/helpers/ImageHelpers.psm1 | 68 +++++---- .../scripts/helpers/InstallHelpers.ps1 | 132 ++++++++++++++-- .../windows/scripts/helpers/PathHelpers.ps1 | 15 +- .../scripts/helpers/VisualStudioHelpers.ps1 | 107 ++++++++++++- .../windows/scripts/tests/Haskell.Tests.ps1 | 8 +- images/windows/scripts/tests/Java.Tests.ps1 | 2 +- 56 files changed, 770 insertions(+), 568 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index cc728e1ea..db907e4ea 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -41,7 +41,7 @@ Here are a few things you can do that will increase the likelihood of your pull ### Windows - Add a script that will install the tool and put the script in the `scripts/build` folder. -There are a bunch of helper functions that could simplify your code: `Choco-Install`, `Install-Binary`, `Install-VsixExtension`, `Start-DownloadWithRetry`, `Test-IsWin19`, `Test-IsWin22` (find the full list of helpers in [ImageHelpers.psm1](images/windows/scripts/helpers/ImageHelpers.psm1)). +There are a bunch of helper functions that could simplify your code: `Install-ChocoPackage`, `Install-Binary`, `Install-VSIXFromFile`, `Install-VSIXFromUrl`, `Invoke-DownloadWithRetry`, `Test-IsWin19`, `Test-IsWin22` (find the full list of helpers in [ImageHelpers.psm1](images/windows/scripts/helpers/ImageHelpers.psm1)). - Add a script that will validate the tool installation and put the script in the `scripts/tests` folder. We use [Pester v5](https://github.com/pester/pester) for validation scripts. If the tests for the tool are complex enough, create a separate `*.Tests.ps1`. Otherwise, use `Tools.Tests.ps1` for simple tests. Add `Invoke-PesterTests -TestFile [-TestName ]` at the end of the installation script to make sure that your tests will be run. diff --git a/images/windows/scripts/build/Configure-PowerShell.ps1 b/images/windows/scripts/build/Configure-PowerShell.ps1 index 32fdb0310..722a1ea89 100644 --- a/images/windows/scripts/build/Configure-PowerShell.ps1 +++ b/images/windows/scripts/build/Configure-PowerShell.ps1 @@ -13,7 +13,7 @@ Set-PSRepository -InstallationPolicy Trusted -Name PSGallery Write-Host 'Warmup PSModuleAnalysisCachePath (speedup first powershell invocation by 20s)' $PSModuleAnalysisCachePath = 'C:\PSModuleAnalysisCachePath\ModuleAnalysisCache' -[Environment]::SetEnvironmentVariable('PSModuleAnalysisCachePath', $PSModuleAnalysisCachePath, [System.EnvironmentVariableTarget]::Machine) +[Environment]::SetEnvironmentVariable('PSModuleAnalysisCachePath', $PSModuleAnalysisCachePath, "Machine") # make variable to be available in the current session ${env:PSModuleAnalysisCachePath} = $PSModuleAnalysisCachePath diff --git a/images/windows/scripts/build/Install-AWSTools.ps1 b/images/windows/scripts/build/Install-AWSTools.ps1 index a5c66e338..e719532e5 100644 --- a/images/windows/scripts/build/Install-AWSTools.ps1 +++ b/images/windows/scripts/build/Install-AWSTools.ps1 @@ -19,7 +19,7 @@ $downloadUrl = Resolve-GithubReleaseAssetUrl ` -Repo "awslabs/aws-sam-cli" ` -Version "latest" ` -UrlMatchPattern "AWS_SAM_CLI_64_PY3.msi" -$externalHash = Get-GithubReleaseAssetHash ` +$externalHash = Get-ChecksumFromGithubRelease ` -Repo "awslabs/aws-sam-cli" ` -Version "latest" ` -FileName (Split-Path $downloadUrl -Leaf) ` diff --git a/images/windows/scripts/build/Install-AliyunCli.ps1 b/images/windows/scripts/build/Install-AliyunCli.ps1 index d4d5c13cb..a922c7230 100644 --- a/images/windows/scripts/build/Install-AliyunCli.ps1 +++ b/images/windows/scripts/build/Install-AliyunCli.ps1 @@ -5,15 +5,17 @@ ################################################################################ Write-Host "Download Latest aliyun-cli archive" -$repoUrl = "https://api.github.com/repos/aliyun/aliyun-cli/releases/latest" -$installerFileName = "aliyun-cli-windows" -$assets = (Invoke-RestMethod -Uri $repoUrl).assets -$downloadUrl = ($assets.browser_download_url -ilike "*aliyun-cli-windows-*-amd64.zip*") | Select-Object -First 1 +$downloadUrl = Resolve-GithubReleaseAssetUrl ` + -Repo "aliyun/aliyun-cli" ` + -Version "latest" ` + -UrlMatchPattern "aliyun-cli-windows-*-amd64.zip" $packagePath = Invoke-DownloadWithRetry $downloadUrl #region Supply chain security - Alibaba Cloud CLI -$hashUrl = ($assets.browser_download_url -ilike "*SHASUMS256.txt*") | Select-Object -First 1 -$externalHash = (Invoke-RestMethod -Uri $hashURL).ToString().Split("`n").Where({ $_ -ilike "*$installerFileName*" }).Split(' ')[0] +$packageName = Split-Path $downloadUrl -Leaf +$externalHash = Get-ChecksumFromUrl -Type "SHA256" ` + -Url ($downloadUrl -replace $packageName, "SHASUMS256.txt") ` + -FileName $packageName Test-FileChecksum $packagePath -ExpectedSHA256Sum $externalHash #endregion diff --git a/images/windows/scripts/build/Install-AzureCli.ps1 b/images/windows/scripts/build/Install-AzureCli.ps1 index 1a1c2f196..6bf7611cf 100644 --- a/images/windows/scripts/build/Install-AzureCli.ps1 +++ b/images/windows/scripts/build/Install-AzureCli.ps1 @@ -7,26 +7,20 @@ Write-Host 'Install the latest Azure CLI release' $azureCliConfigPath = 'C:\azureCli' # Store azure-cli cache outside of the provisioning user's profile -[Environment]::SetEnvironmentVariable('AZURE_CONFIG_DIR', $azureCliConfigPath, [System.EnvironmentVariableTarget]::Machine) -# make variable to be available in the current session -${env:AZURE_CONFIG_DIR} = $azureCliConfigPath +[Environment]::SetEnvironmentVariable('AZURE_CONFIG_DIR', $azureCliConfigPath, "Machine") + +$azureCliExtensionPath = Join-Path $Env:CommonProgramFiles 'AzureCliExtensionDirectory' +New-Item -ItemType 'Directory' -Path $azureCliExtensionPath | Out-Null +[Environment]::SetEnvironmentVariable('AZURE_EXTENSION_DIR', $azureCliExtensionPath, "Machine") Install-Binary -Type MSI ` -Url 'https://aka.ms/installazurecliwindowsx64' ` -ExpectedSignature '72105B6D5F370B62FD5C82F1512F7AD7DEE5F2C0' -$azureCliExtensionPath = Join-Path $Env:CommonProgramFiles 'AzureCliExtensionDirectory' -$null = New-Item -ItemType 'Directory' -Path $azureCliExtensionPath - -[Environment]::SetEnvironmentVariable('AZURE_EXTENSION_DIR', $azureCliExtensionPath, [System.EnvironmentVariableTarget]::Machine) -# make variable to be available in the current session -${env:AZURE_EXTENSION_DIR} = $azureCliExtensionPath +Update-Environment # Warm-up Azure CLI - Write-Host "Warmup 'az'" - -$env:PATH = [Environment]::GetEnvironmentVariable('PATH', 'Machine') az --help | Out-Null if ($LASTEXITCODE -ne 0) { throw "Command 'az --help' failed" diff --git a/images/windows/scripts/build/Install-AzureDevOpsCli.ps1 b/images/windows/scripts/build/Install-AzureDevOpsCli.ps1 index 8227a3fc8..f911ba6e7 100644 --- a/images/windows/scripts/build/Install-AzureDevOpsCli.ps1 +++ b/images/windows/scripts/build/Install-AzureDevOpsCli.ps1 @@ -5,34 +5,26 @@ $azureDevOpsCliConfigPath = 'C:\azureDevOpsCli' # Store azure-devops-cli cache outside of the provisioning user's profile -[Environment]::SetEnvironmentVariable('AZ_DEVOPS_GLOBAL_CONFIG_DIR', $azureDevOpsCliConfigPath, [System.EnvironmentVariableTarget]::Machine) -# make variable to be available in the current session -${env:AZ_DEVOPS_GLOBAL_CONFIG_DIR} = $azureDevOpsCliConfigPath +[Environment]::SetEnvironmentVariable('AZ_DEVOPS_GLOBAL_CONFIG_DIR', $azureDevOpsCliConfigPath, "Machine") $azureDevOpsCliCachePath = Join-Path $azureDevOpsCliConfigPath 'cache' -$null = New-Item -ItemType 'Directory' -Path $azureDevOpsCliCachePath +New-Item -ItemType 'Directory' -Path $azureDevOpsCliCachePath | Out-Null +[Environment]::SetEnvironmentVariable('AZURE_DEVOPS_CACHE_DIR', $azureDevOpsCliCachePath, "Machine") -[Environment]::SetEnvironmentVariable('AZURE_DEVOPS_CACHE_DIR', $azureDevOpsCliCachePath, [System.EnvironmentVariableTarget]::Machine) -# make variable to be available in the current session -${env:AZURE_DEVOPS_CACHE_DIR} = $azureDevOpsCliCachePath +Update-Environment az extension add -n azure-devops -if ($LASTEXITCODE -ne 0) -{ +if ($LASTEXITCODE -ne 0) { throw "Command 'az extension add -n azure-devops' failed" } # Warm-up Azure DevOps CLI - Write-Host "Warmup 'az-devops'" @('devops', 'pipelines', 'boards', 'repos', 'artifacts') | ForEach-Object { - - az $_ --help - if ($LASTEXITCODE -ne 0) - { - throw "Command 'az $_ --help' failed" - } - + az $_ --help + if ($LASTEXITCODE -ne 0) { + throw "Command 'az $_ --help' failed" + } } # calling az devops login to force it to install `keyring`. Login will actually fail, redirecting error to null diff --git a/images/windows/scripts/build/Install-Bazel.ps1 b/images/windows/scripts/build/Install-Bazel.ps1 index bc3a4e011..ad922de69 100644 --- a/images/windows/scripts/build/Install-Bazel.ps1 +++ b/images/windows/scripts/build/Install-Bazel.ps1 @@ -6,5 +6,8 @@ Install-ChocoPackage bazel npm install -g @bazel/bazelisk +if ($LASTEXITCODE -ne 0) { + throw "Command 'npm install -g @bazel/bazelisk' failed" +} Invoke-PesterTests -TestFile "Tools" -TestName "Bazel" diff --git a/images/windows/scripts/build/Install-Chocolatey.ps1 b/images/windows/scripts/build/Install-Chocolatey.ps1 index 2f9a4b873..742fa3a00 100644 --- a/images/windows/scripts/build/Install-Chocolatey.ps1 +++ b/images/windows/scripts/build/Install-Chocolatey.ps1 @@ -7,20 +7,10 @@ Write-Host "Set TLS1.2" [Net.ServicePointManager]::SecurityProtocol = [Net.ServicePointManager]::SecurityProtocol -bor "Tls12" Write-Host "Install chocolatey" -$chocoExePath = 'C:\ProgramData\Chocolatey\bin' # Add to system PATH -$systemPath = [Environment]::GetEnvironmentVariable('Path', [System.EnvironmentVariableTarget]::Machine) -$systemPath += ';' + $chocoExePath -[Environment]::SetEnvironmentVariable("PATH", $systemPath, [System.EnvironmentVariableTarget]::Machine) - -# Update local process' path -$userPath = [Environment]::GetEnvironmentVariable('Path', [System.EnvironmentVariableTarget]::User) -if ($userPath) { - $env:Path = $systemPath + ";" + $userPath -} else { - $env:Path = $systemPath -} +Add-MachinePathItem 'C:\ProgramData\Chocolatey\bin' +Update-Environment # Verify and run choco installer $signatureThumbprint = "83AC7D88C66CB8680BCE802E0F0F5C179722764B" diff --git a/images/windows/scripts/build/Install-Chrome.ps1 b/images/windows/scripts/build/Install-Chrome.ps1 index 52cd2a5ea..25687abe8 100644 --- a/images/windows/scripts/build/Install-Chrome.ps1 +++ b/images/windows/scripts/build/Install-Chrome.ps1 @@ -33,8 +33,7 @@ $regGoogleParameters = @( $regGoogleParameters | ForEach-Object { $Arguments = $_ - if (-not ($Arguments.Path)) - { + if (-not ($Arguments.Path)) { $Arguments.Add("Path", $regGoogleUpdatePath) } $Arguments.Add("Force", $true) @@ -44,15 +43,14 @@ $regGoogleParameters | ForEach-Object { # Install Chrome WebDriver Write-Host "Install Chrome WebDriver..." $ChromeDriverPath = "$($env:SystemDrive)\SeleniumWebDrivers\ChromeDriver" -if (-not (Test-Path -Path $ChromeDriverPath)) -{ +if (-not (Test-Path -Path $ChromeDriverPath)) { New-Item -Path $ChromeDriverPath -ItemType Directory -Force } Write-Host "Get the Chrome WebDriver download URL..." $RegistryPath = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths" $ChromePath = (Get-ItemProperty "$RegistryPath\chrome.exe").'(default)' -[version]$ChromeVersion = [System.Diagnostics.FileVersionInfo]::GetVersionInfo($ChromePath).ProductVersion +[version] $ChromeVersion = [System.Diagnostics.FileVersionInfo]::GetVersionInfo($ChromePath).ProductVersion $ChromeBuild = "$($ChromeVersion.Major).$($ChromeVersion.Minor).$($ChromeVersion.Build)" $ChromeDriverVersionsUrl = "https://googlechromelabs.github.io/chrome-for-testing/latest-patch-versions-per-build-with-downloads.json" @@ -63,7 +61,7 @@ $ChromeDriverVersion = $ChromeDriverVersions.builds.$ChromeBuild if (-not ($ChromeDriverVersion)) { $availableVersions = $ChromeDriverVersions.builds | Get-Member | Select-Object -ExpandProperty Name Write-Host "Available chromedriver builds are $availableVersions" - Throw "Can't determine chromedriver version that matches chrome build $ChromeBuild" + throw "Can't determine chromedriver version that matches chrome build $ChromeBuild" } $ChromeDriverVersion.version | Out-File -FilePath "$ChromeDriverPath\versioninfo.txt" -Force; @@ -79,10 +77,7 @@ Expand-7ZipArchive -Path $ChromeDriverArchPath -DestinationPath $ChromeDriverPat Write-Host "Setting the environment variables..." [Environment]::SetEnvironmentVariable("ChromeWebDriver", $ChromeDriverPath, "Machine") - -$regEnvKey = 'HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager\Environment\' -$PathValue = Get-ItemPropertyValue -Path $regEnvKey -Name 'Path' -$PathValue += ";$ChromeDriverPath\" -Set-ItemProperty -Path $regEnvKey -Name 'Path' -Value $PathValue +Add-MachinePathItem $ChromeDriverPath +Update-Environment Invoke-PesterTests -TestFile "Browsers" -TestName "Chrome" diff --git a/images/windows/scripts/build/Install-Docker.ps1 b/images/windows/scripts/build/Install-Docker.ps1 index fb6ad34b8..cc353a1b7 100644 --- a/images/windows/scripts/build/Install-Docker.ps1 +++ b/images/windows/scripts/build/Install-Docker.ps1 @@ -6,15 +6,16 @@ ################################################################################ Write-Host "Get latest Moby release" -$mobyLatestReleaseVersion = (Invoke-RestMethod -Uri "https://api.github.com/repos/moby/moby/releases/latest").tag_name.Trim("v") +$mobyLatestVersion = (Get-GithubReleasesByVersion -Repo "moby/moby" -Version "latest").version + $dockerceUrl = "https://download.docker.com/win/static/stable/x86_64/" $dockerceBinaries = Invoke-WebRequest -Uri $dockerceUrl -UseBasicParsing -Write-Host "Check Moby version $mobyLatestReleaseVersion" -$mobyRelease = $dockerceBinaries.Links.href -match "${mobyLatestReleaseVersion}\.zip" | Select-Object -Last 1 +Write-Host "Check Moby version $mobyLatestVersion" +$mobyRelease = $dockerceBinaries.Links.href -match "${mobyLatestVersion}\.zip" | Select-Object -Last 1 if (-not $mobyRelease) { Write-Host "Release not found for $mobyLatestRelease version" - $versions = [regex]::Matches($dockerceBinaries.Links.href, "docker-(\d+\.\d+\.\d+)\.zip") | Sort-Object { [version]$_.Groups[1].Value } + $versions = [regex]::Matches($dockerceBinaries.Links.href, "docker-(\d+\.\d+\.\d+)\.zip") | Sort-Object { [version] $_.Groups[1].Value } $mobyRelease = $versions | Select-Object -ExpandProperty Value -Last 1 Write-Host "Found $mobyRelease" } @@ -47,8 +48,7 @@ foreach ($dockerImage in $dockerImages) { docker pull $dockerImage if (!$?) { - Write-Host "Docker pull failed with a non-zero exit code" - exit 1 + throw "Docker pull failed with a non-zero exit code ($LastExitCode)" } } diff --git a/images/windows/scripts/build/Install-DockerWinCred.ps1 b/images/windows/scripts/build/Install-DockerWinCred.ps1 index 127d4e488..e4d8dd6eb 100644 --- a/images/windows/scripts/build/Install-DockerWinCred.ps1 +++ b/images/windows/scripts/build/Install-DockerWinCred.ps1 @@ -4,32 +4,19 @@ ## Supply chain security: checksum validation ################################################################################ -#region functions -function Get-DockerWincredHash { - Param ( - [Parameter(Mandatory = $True)] - [string] $Release - ) - - $hashURL = "https://github.com/docker/docker-credential-helpers/releases/download/${Release}/checksums.txt" - (Invoke-RestMethod -Uri $hashURL).ToString().Split("`n").Where({ $_ -ilike "*docker-credential-wincred-${Release}.windows-amd64.exe*" }).Split(' ')[0] -} -#endregion - Write-Host "Install docker-wincred" -$dockerCredLatestRelease = Invoke-RestMethod -Uri "https://api.github.com/repos/docker/docker-credential-helpers/releases/latest" -$dockerCredDownloadUrl = $dockerCredLatestRelease.assets.browser_download_url -match "docker-credential-wincred-.+\.exe" | Select-Object -First 1 -Invoke-DownloadWithRetry -Url $dockerCredDownloadUrl -Path "C:\Windows\System32\docker-credential-wincred.exe" +$downloadUrl = Resolve-GithubReleaseAssetUrl ` + -Repo "docker/docker-credential-helpers" ` + -Version "latest" ` + -UrlMatchPattern "docker-credential-wincred-*amd64.exe" +$binaryPath = Invoke-DownloadWithRetry -Url $downloadUrl -Path "C:\Windows\System32\docker-credential-wincred.exe" #region Supply chain security -$distributor_file_hash = Get-DockerWincredHash -Release $dockerCredLatestRelease.name -$local_file_hash = (Get-FileHash -Path 'C:\Windows\System32\docker-credential-wincred.exe' -Algorithm SHA256).Hash - -if ($local_file_hash -ne $distributor_file_hash) { - Write-Host "hash must be equal to: ${distributor_file_hash}" - Write-Host "actual hash is: ${local_file_hash}" - throw 'Checksum verification failed, please rerun install' -} +$binaryName = Split-Path $downloadUrl -Leaf +$externalHash = Get-ChecksumFromUrl -Type "SHA256" ` + -Url ($downloadUrl -replace $binaryName, "checksums.txt") ` + -FileName $binaryName +Test-FileChecksum -Path $binaryPath -ExpectedSHA256Sum $externalHash #endregion Invoke-PesterTests -TestFile "Docker" -TestName "DockerWinCred" diff --git a/images/windows/scripts/build/Install-DotnetSDK.ps1 b/images/windows/scripts/build/Install-DotnetSDK.ps1 index d4198c92b..1cc9645e3 100644 --- a/images/windows/scripts/build/Install-DotnetSDK.ps1 +++ b/images/windows/scripts/build/Install-DotnetSDK.ps1 @@ -6,36 +6,40 @@ ################################################################################ # Set environment variables -[System.Environment]::SetEnvironmentVariable("DOTNET_MULTILEVEL_LOOKUP", "0", "Machine") -[System.Environment]::SetEnvironmentVariable("DOTNET_NOLOGO", "1", "Machine") -[System.Environment]::SetEnvironmentVariable("DOTNET_SKIP_FIRST_TIME_EXPERIENCE", "1", "Machine") +[Environment]::SetEnvironmentVariable("DOTNET_MULTILEVEL_LOOKUP", "0", "Machine") +[Environment]::SetEnvironmentVariable("DOTNET_NOLOGO", "1", "Machine") +[Environment]::SetEnvironmentVariable("DOTNET_SKIP_FIRST_TIME_EXPERIENCE", "1", "Machine") [Net.ServicePointManager]::SecurityProtocol = [Net.ServicePointManager]::SecurityProtocol -bor "Tls12" #region "Functions" -function Get-SDKVersionsToInstall ( - $DotnetVersion -) { - $metadataJsonUri = "https://dotnetcli.blob.core.windows.net/dotnet/release-metadata/${DotnetVersion}/releases.json" - $currentReleases = Invoke-DownloadWithRetry $metadataJsonUri | Get-Item | Get-Content | ConvertFrom-Json +function Get-SDKVersionsToInstall { + param ( + [Parameter(Mandatory)] + [string] $DotnetVersion + ) + $releasesJsonUri = "https://dotnetcli.blob.core.windows.net/dotnet/release-metadata/${DotnetVersion}/releases.json" + $releasesData = (Invoke-DownloadWithRetry $releasesJsonUri) | Get-Item | Get-Content | ConvertFrom-Json # filtering out the preview/rc releases - $currentReleases = $currentReleases.'releases' | Where-Object { !$_.'release-version'.Contains('-') } + $releases = $releasesData.'releases' | Where-Object { !$_.'release-version'.Contains('-') } $sdks = @() - foreach ($release in $currentReleases) { + foreach ($release in $releases) { $sdks += $release.'sdk' $sdks += $release.'sdks' } return $sdks.version ` - | Sort-Object { [Version] $_ } -Unique ` - | Group-Object { $_.Substring(0, $_.LastIndexOf('.') + 2) } ` - | ForEach-Object { $_.Group[-1] } + | Sort-Object { [Version] $_ } -Unique ` + | Group-Object { $_.Substring(0, $_.LastIndexOf('.') + 2) } ` + | ForEach-Object { $_.Group[-1] } } -function Invoke-Warmup ( - $SdkVersion -) { +function Invoke-DotnetWarmup { + param ( + [Parameter(Mandatory)] + [string] $SDKVersion + ) # warm up dotnet for first time experience $projectTypes = @('console', 'mstest', 'web', 'mvc', 'webapi') $projectTypes | ForEach-Object { @@ -43,85 +47,76 @@ function Invoke-Warmup ( $projectPath = Join-Path -Path C:\temp -ChildPath $template New-Item -Path $projectPath -Force -ItemType Directory Push-Location -Path $projectPath - & $env:ProgramFiles\dotnet\dotnet.exe new globaljson --sdk-version "$sdkVersion" + & $env:ProgramFiles\dotnet\dotnet.exe new globaljson --sdk-version "$SDKVersion" & $env:ProgramFiles\dotnet\dotnet.exe new $template Pop-Location Remove-Item $projectPath -Force -Recurse } } -function InstallSDKVersion ( - $SdkVersion, - $dotnetVersion, - $Warmup -) { - if (!(Test-Path -Path "C:\Program Files\dotnet\sdk\$sdkVersion")) { - Write-Host "Installing dotnet $sdkVersion" - $ZipPath = [System.IO.Path]::combine([System.IO.Path]::GetTempPath(), [System.IO.Path]::GetRandomFileName()) - .\dotnet-install.ps1 -Version $sdkVersion -InstallDir $(Join-Path -Path $env:ProgramFiles -ChildPath 'dotnet') -ZipPath $ZipPath -KeepZip +function Install-DotnetSDK { + param ( + [Parameter(Mandatory)] + [string] $InstallScriptPath, + [Parameter(Mandatory)] + [Alias('Version')] + [string] $SDKVersion, + [Parameter(Mandatory)] + [string] $DotnetVersion + ) - #region Supply chain security - $distributorFileHash = (Invoke-RestMethod -Uri "https://dotnetcli.blob.core.windows.net/dotnet/release-metadata/$dotnetVersion/releases.json").releases.sdks.Where({ $_.version -eq $SdkVersion }).files.Where({ $_.name -eq 'dotnet-sdk-win-x64.zip' }).hash - Test-FileChecksum $ZipPath -ExpectedSHA512Sum $distributorFileHash - #endregion - } else { - Write-Host "Sdk version $sdkVersion already installed" + if (Test-Path -Path "C:\Program Files\dotnet\sdk\$SDKVersion") { + Write-Host "Sdk version $SDKVersion already installed" + return } - if ($Warmup) { - Invoke-Warmup -SdkVersion $SdkVersion - } + Write-Host "Installing dotnet $SDKVersion" + $zipPath = Join-Path ([IO.Path]::GetTempPath()) ([IO.Path]::GetRandomFileName()) + & $InstallScriptPath -Version $SDKVersion -InstallDir $(Join-Path -Path $env:ProgramFiles -ChildPath 'dotnet') -ZipPath $zipPath -KeepZip + + #region Supply chain security + $releasesJsonUri = "https://dotnetcli.blob.core.windows.net/dotnet/release-metadata/${DotnetVersion}/releases.json" + $releasesData = (Invoke-DownloadWithRetry $releasesJsonUri) | Get-Item | Get-Content | ConvertFrom-Json + $distributorFileHash = $releasesData.releases.sdks.Where({ $_.version -eq $SDKVersion }).files.Where({ $_.name -eq 'dotnet-sdk-win-x64.zip' }).hash + Test-FileChecksum $zipPath -ExpectedSHA512Sum $distributorFileHash + #endregion } +#endregion -function InstallAllValidSdks() { - # Consider all channels except preview/eol channels. - # Sort the channels in ascending order - $dotnetToolset = (Get-ToolsetContent).dotnet - $dotnetVersions = $dotnetToolset.versions - $warmup = $dotnetToolset.warmup +$dotnetToolset = (Get-ToolsetContent).dotnet - # Download installation script. - $installationName = "dotnet-install.ps1" - $installationUrl = "https://dot.net/v1/${installationName}" - Invoke-DownloadWithRetry -Url $installationUrl -Path ".\$installationName" +# Download installation script. +$installScriptPath = Invoke-DownloadWithRetry -Url "https://dot.net/v1/dotnet-install.ps1" - foreach ($dotnetVersion in $dotnetVersions) { - $sdkVersionsToInstall = Get-SDKVersionsToInstall -DotnetVersion $dotnetVersion - foreach ($sdkVersion in $sdkVersionsToInstall) { - InstallSDKVersion -SdkVersion $sdkVersion -DotnetVersion $dotnetVersion -Warmup $warmup +# Install and warm up dotnet +foreach ($dotnetVersion in $dotnetToolset.versions) { + $sdkVersionsToInstall = Get-SDKVersionsToInstall -DotnetVersion $dotnetVersion + foreach ($sdkVersion in $sdkVersionsToInstall) { + Install-DotnetSDK -InstallScriptPath $installScriptPath -SDKVersion $sdkVersion -DotnetVersion $dotnetVersion + if ($dotnetToolset.warmup) { + Invoke-DotnetWarmup -SDKVersion $sdkVersion } } } -function InstallTools() { - $dotnetTools = (Get-ToolsetContent).dotnet.tools +# Add dotnet to PATH +Add-MachinePathItem "C:\Program Files\dotnet" - foreach ($dotnetTool in $dotnetTools) { - dotnet tool install $($dotnetTool.name) --tool-path "C:\Users\Default\.dotnet\tools" --add-source https://api.nuget.org/v3/index.json | Out-Null - } +# Remove NuGet Folder +$nugetPath = "$env:APPDATA\NuGet" +if (Test-Path $nugetPath) { + Remove-Item -Path $nugetPath -Force -Recurse } -function RunPostInstallationSteps() { - # Add dotnet to PATH - Add-MachinePathItem "C:\Program Files\dotnet" +# Generate and copy new NuGet.Config config +dotnet nuget list source | Out-Null +Copy-Item -Path $nugetPath -Destination C:\Users\Default\AppData\Roaming -Force -Recurse - # Remove NuGet Folder - $nugetPath = "$env:APPDATA\NuGet" - if (Test-Path $nugetPath) { - Remove-Item -Path $nugetPath -Force -Recurse - } - - # Generate and copy new NuGet.Config config - dotnet nuget list source | Out-Null - Copy-Item -Path $nugetPath -Destination C:\Users\Default\AppData\Roaming -Force -Recurse - - # Add %USERPROFILE%\.dotnet\tools to USER PATH - Add-DefaultPathItem "%USERPROFILE%\.dotnet\tools" +# Install dotnet tools +Write-Host "Installing dotnet tools" +Add-DefaultPathItem "%USERPROFILE%\.dotnet\tools" +foreach ($dotnetTool in $dotnetToolset.tools) { + dotnet tool install $($dotnetTool.name) --tool-path "C:\Users\Default\.dotnet\tools" --add-source https://api.nuget.org/v3/index.json | Out-Null } -#endregion - -InstallAllValidSdks -RunPostInstallationSteps -InstallTools Invoke-PesterTests -TestFile "DotnetSDK" diff --git a/images/windows/scripts/build/Install-Firefox.ps1 b/images/windows/scripts/build/Install-Firefox.ps1 index b9c847774..eca81779f 100644 --- a/images/windows/scripts/build/Install-Firefox.ps1 +++ b/images/windows/scripts/build/Install-Firefox.ps1 @@ -11,7 +11,10 @@ $VersionsManifest = Invoke-RestMethod "https://product-details.mozilla.org/1.0/f Write-Host "Install Firefox browser..." $installerUrl = "https://download.mozilla.org/?product=firefox-$($VersionsManifest.LATEST_FIREFOX_VERSION)&os=win64&lang=en-US" $hashUrl = "https://archive.mozilla.org/pub/firefox/releases/$($VersionsManifest.LATEST_FIREFOX_VERSION)/SHA256SUMS" -$externalHash = (Invoke-RestMethod -Uri $hashURL).ToString().Split("`n").Where({ $_ -ilike "*win64/en-US/Firefox Setup*exe*" }).Split(' ')[0] + +$externalHash = Get-ChecksumFromUrl -Type "SHA256" ` + -Url $hashUrl ` + -FileName "win64/en-US/Firefox Setup*exe" Install-Binary -Type EXE ` -Url $installerUrl ` @@ -36,13 +39,14 @@ if (-not (Test-Path -Path $GeckoDriverPath)) { } Write-Host "Get the Gecko WebDriver version..." -$GeckoDriverJson = Invoke-RestMethod "https://api.github.com/repos/mozilla/geckodriver/releases/latest" -$GeckoDriverWindowsAsset = $GeckoDriverJson.assets | Where-Object { $_.name -Match "win64" } | Select-Object -First 1 -$GeckoDriverVersion = $GeckoDriverJson.tag_name -$GeckoDriverVersion.Substring(1) | Out-File -FilePath "$GeckoDriverPath\versioninfo.txt" -Force; +$GeckoDriverVersion = (Get-GithubReleasesByVersion -Repo "mozilla/geckodriver" -Version "latest").version +$GeckoDriverVersion | Out-File -FilePath "$GeckoDriverPath\versioninfo.txt" -Force Write-Host "Download Gecko WebDriver WebDriver..." -$GeckoDriverDownloadUrl = $GeckoDriverWindowsAsset.browser_download_url +$GeckoDriverDownloadUrl = Resolve-GithubReleaseAssetUrl ` + -Repo "mozilla/geckodriver" ` + -Version $GeckoDriverVersion ` + -UrlMatchPattern "geckodriver-*-win64.zip" $GeckoDriverArchPath = Invoke-DownloadWithRetry $GeckoDriverDownloadUrl Write-Host "Expand Gecko WebDriver archive..." diff --git a/images/windows/scripts/build/Install-Git.ps1 b/images/windows/scripts/build/Install-Git.ps1 index 0a2c4234a..1d2186946 100644 --- a/images/windows/scripts/build/Install-Git.ps1 +++ b/images/windows/scripts/build/Install-Git.ps1 @@ -11,7 +11,7 @@ $downloadUrl = Resolve-GithubReleaseAssetUrl ` -Version "latest" ` -UrlMatchPattern "Git-*-64-bit.exe" -$externalHash = Get-GithubReleaseAssetHash ` +$externalHash = Get-ChecksumFromGithubRelease ` -Repo "git-for-windows/git" ` -Version "latest" ` -FileName (Split-Path $downloadUrl -Leaf) ` @@ -37,7 +37,7 @@ Update-Environment git config --system --add safe.directory "*" # Disable GCM machine-wide -[Environment]::SetEnvironmentVariable("GCM_INTERACTIVE", "Never", [System.EnvironmentVariableTarget]::Machine) +[Environment]::SetEnvironmentVariable("GCM_INTERACTIVE", "Never", "Machine") # Add to PATH Add-MachinePathItem "C:\Program Files\Git\bin" diff --git a/images/windows/scripts/build/Install-GitHub-CLI.ps1 b/images/windows/scripts/build/Install-GitHub-CLI.ps1 index d34412e35..0d1d80fd5 100644 --- a/images/windows/scripts/build/Install-GitHub-CLI.ps1 +++ b/images/windows/scripts/build/Install-GitHub-CLI.ps1 @@ -6,16 +6,22 @@ Write-Host "Get the latest gh version..." -$repoUrl = "https://api.github.com/repos/cli/cli/releases/latest" -$assets = (Invoke-RestMethod -Uri $repoUrl).assets -$downloadUrl = ($assets.browser_download_url -match "windows_amd64.msi") | Select-Object -First 1 +$downloadUrl = Resolve-GithubReleaseAssetUrl ` + -Repo "cli/cli" ` + -Version "latest" ` + -UrlMatchPattern "gh_*_windows_amd64.msi" -$hashUrl = ($assets.browser_download_url -match "checksums.txt") | Select-Object -First 1 -$externalHash = (Invoke-RestMethod -Uri $hashURL).ToString().Split("`n").Where({ $_ -ilike "*windows_amd64.msi*" }).Split(' ')[0] +$checksumsUrl = Resolve-GithubReleaseAssetUrl ` + -Repo "cli/cli" ` + -Version "latest" ` + -UrlMatchPattern "gh_*_checksums.txt" +$externalHash = Get-ChecksumFromUrl -Type "SHA256" ` + -Url $checksumsUrl ` + -FileName (Split-Path $downloadUrl -Leaf) Install-Binary ` - -Url $downloadUrl ` - -ExpectedSHA256Sum $externalHash + -Url $downloadUrl ` + -ExpectedSHA256Sum $externalHash Add-MachinePathItem "C:\Program Files (x86)\GitHub CLI" diff --git a/images/windows/scripts/build/Install-Haskell.ps1 b/images/windows/scripts/build/Install-Haskell.ps1 index ba0219be5..583c89ebf 100644 --- a/images/windows/scripts/build/Install-Haskell.ps1 +++ b/images/windows/scripts/build/Install-Haskell.ps1 @@ -22,9 +22,9 @@ New-Item -Path "$ghcupPrefix\ghcup" -ItemType 'directory' -ErrorAction SilentlyC New-Item -Path "$ghcupPrefix\ghcup\bin" -ItemType 'directory' -ErrorAction SilentlyContinue | Out-Null Invoke-DownloadWithRetry -Url $ghcupDownloadURL -Path "$ghcupPrefix\ghcup\bin\ghcup.exe" -[System.Environment]::SetEnvironmentVariable("GHCUP_INSTALL_BASE_PREFIX", $ghcupPrefix, "Machine") -[System.Environment]::SetEnvironmentVariable("GHCUP_MSYS2", $msysPath, "Machine") -[System.Environment]::SetEnvironmentVariable("CABAL_DIR", $cabalDir, "Machine") +[Environment]::SetEnvironmentVariable("GHCUP_INSTALL_BASE_PREFIX", $ghcupPrefix, "Machine") +[Environment]::SetEnvironmentVariable("GHCUP_MSYS2", $msysPath, "Machine") +[Environment]::SetEnvironmentVariable("CABAL_DIR", $cabalDir, "Machine") Add-MachinePathItem "$ghcupPrefix\ghcup\bin" Add-MachinePathItem "$cabalDir\bin" Update-Environment @@ -32,12 +32,11 @@ Update-Environment # Get 3 latest versions of GHC $Versions = ghcup list -t ghc -r | Where-Object {$_ -notlike "prerelease"} $VersionsOutput = [Version[]]($Versions | ForEach-Object{ $_.Split(' ')[1]; }) -$LatestMajorMinor = $VersionsOutput | Group-Object { $_.ToString(2) } | Sort-Object { [Version]$_.Name } | Select-Object -last 3 +$LatestMajorMinor = $VersionsOutput | Group-Object { $_.ToString(2) } | Sort-Object { [Version] $_.Name } | Select-Object -last 3 $VersionsList = $LatestMajorMinor | ForEach-Object { $_.Group | Select-Object -Last 1 } | Sort-Object # The latest version will be installed as a default -ForEach ($version in $VersionsList) -{ +foreach ($version in $VersionsList) { Write-Host "Installing ghc $version..." ghcup install ghc $version ghcup set ghc $version diff --git a/images/windows/scripts/build/Install-IEWebDriver.ps1 b/images/windows/scripts/build/Install-IEWebDriver.ps1 index 32dd00ccd..1008f499d 100644 --- a/images/windows/scripts/build/Install-IEWebDriver.ps1 +++ b/images/windows/scripts/build/Install-IEWebDriver.ps1 @@ -15,7 +15,7 @@ $driverZipFile = Invoke-DownloadWithRetry $ieDriverUrl $ieDriverPath = "C:\SeleniumWebDrivers\IEDriver" if (-not (Test-Path -Path $ieDriverPath)) { - $null = New-Item -Path $ieDriverPath -ItemType Directory -Force + New-Item -Path $ieDriverPath -ItemType Directory -Force | Out-Null } Expand-7ZipArchive -Path $driverZipFile -DestinationPath $ieDriverPath diff --git a/images/windows/scripts/build/Install-JavaTools.ps1 b/images/windows/scripts/build/Install-JavaTools.ps1 index 1a9572876..655e6ad07 100644 --- a/images/windows/scripts/build/Install-JavaTools.ps1 +++ b/images/windows/scripts/build/Install-JavaTools.ps1 @@ -39,7 +39,7 @@ function Set-JavaPath { $newPath = $javaPath + '\bin;' + $newPath Write-Host "Add $javaPath\bin to PATH" - [System.Environment]::SetEnvironmentVariable("PATH", $newPath, "Machine") + [Environment]::SetEnvironmentVariable("PATH", $newPath, "Machine") Write-Host "Set JAVA_HOME environmental variable as $javaPath" [Environment]::SetEnvironmentVariable("JAVA_HOME", $javaPath, "Machine") @@ -118,13 +118,13 @@ Install-ChocoPackage maven -ArgumentList "--version=$versionToInstall" Install-ChocoPackage gradle # Add maven env variables to Machine -[string] $m2 = ([Environment]::GetEnvironmentVariable("PATH", "Machine")).Split(";") -match "maven" +[string] $m2Path = ([Environment]::GetEnvironmentVariable("PATH", "Machine")).Split(";") -match "maven" -$m2_repo = 'C:\ProgramData\m2' -New-Item -Path $m2_repo -ItemType Directory -Force | Out-Null +$m2RepoPath = 'C:\ProgramData\m2' +New-Item -Path $m2RepoPath -ItemType Directory -Force | Out-Null -[Environment]::SetEnvironmentVariable("M2", $m2, "Machine") -[Environment]::SetEnvironmentVariable("M2_REPO", $m2_repo, "Machine") +[Environment]::SetEnvironmentVariable("M2", $m2Path, "Machine") +[Environment]::SetEnvironmentVariable("M2_REPO", $m2RepoPath, "Machine") [Environment]::SetEnvironmentVariable("MAVEN_OPTS", "-Xms256m", "Machine") # Download cobertura jars diff --git a/images/windows/scripts/build/Install-Kotlin.ps1 b/images/windows/scripts/build/Install-Kotlin.ps1 index 9ec855408..091891340 100644 --- a/images/windows/scripts/build/Install-Kotlin.ps1 +++ b/images/windows/scripts/build/Install-Kotlin.ps1 @@ -14,7 +14,7 @@ $kotlinDownloadUrl = Resolve-GithubReleaseAssetUrl ` $kotlinArchivePath = Invoke-DownloadWithRetry $kotlinDownloadUrl #region Supply chain security -$externalHash = Get-GithubReleaseAssetHash ` +$externalHash = Get-ChecksumFromGithubRelease ` -Repo "JetBrains/kotlin" ` -Version "$kotlinVersion" ` -FileName (Split-Path $kotlinDownloadUrl -Leaf) ` diff --git a/images/windows/scripts/build/Install-KubernetesTools.ps1 b/images/windows/scripts/build/Install-KubernetesTools.ps1 index 529957f7a..59c408706 100644 --- a/images/windows/scripts/build/Install-KubernetesTools.ps1 +++ b/images/windows/scripts/build/Install-KubernetesTools.ps1 @@ -6,20 +6,24 @@ Write-Host "Install Kind" # Choco installation can't be used because it depends on docker-desktop -$repoUrl = 'https://api.github.com/repos/kubernetes-sigs/kind/releases/latest' -$assets = (Invoke-RestMethod -Uri $repoUrl).assets -[System.String] $kindDownloadLink = $assets.browser_download_url -match "kind-windows-amd64$" -$destFilePath = "C:\ProgramData\kind" -$null = New-Item -Path $destFilePath -ItemType Directory -Force -$packagePath = Invoke-DownloadWithRetry -Url $kindDownloadLink -Path "$destFilePath\kind.exe" + +$targetDir = "C:\ProgramData\kind" +New-Item -Path $targetDir -ItemType Directory -Force | Out-Null + +$downloadUrl = Resolve-GithubReleaseAssetUrl ` + -Repo "kubernetes-sigs/kind" ` + -Version "latest" ` + -UrlMatchPattern "kind-windows-amd64" +$packagePath = Invoke-DownloadWithRetry -Url $downloadUrl -Path "$targetDir\kind.exe" #region Supply chain security - Kind -$hashUrl = ($assets.browser_download_url -match "kind-windows-amd64.sha256sum") | Select-Object -First 1 -$externalHash = (Invoke-RestMethod -Uri $hashURL).ToString().Split("`n").Where({ $_ -ilike "*kind-windows-amd64*" }).Split(' ')[0] +$externalHash = Get-ChecksumFromUrl -Type "SHA256" ` + -Url "$downloadUrl.sha256sum" ` + -FileName (Split-Path $downloadUrl -Leaf) Test-FileChecksum $packagePath -ExpectedSHA256Sum $externalHash #endregion -Add-MachinePathItem $destFilePath +Add-MachinePathItem $targetDir Write-Host "Install Kubectl" Install-ChocoPackage kubernetes-cli diff --git a/images/windows/scripts/build/Install-Mingw64.ps1 b/images/windows/scripts/build/Install-Mingw64.ps1 index cbb9b29d3..2d5279b09 100644 --- a/images/windows/scripts/build/Install-Mingw64.ps1 +++ b/images/windows/scripts/build/Install-Mingw64.ps1 @@ -4,67 +4,67 @@ ################################################################################ if (Test-IsWin19) { - # If Windows 2019, install version 8.1.0 form sourceforge - $baseUrl = "https://sourceforge.net/projects/mingw-w64/files" + # If Windows 2019, install version 8.1.0 form sourceforge + $baseUrl = "https://sourceforge.net/projects/mingw-w64/files" - $("mingw32", "mingw64") | ForEach-Object { - if ($_ -eq "mingw32") { - $url = "$baseUrl/Toolchains%20targetting%20Win32/Personal%20Builds/mingw-builds/8.1.0/threads-posix/dwarf/i686-8.1.0-release-posix-dwarf-rt_v6-rev0.7z/download" - $sha256sum = 'adb84b70094c0225dd30187ff995e311d19424b1eb8f60934c60e4903297f946' - } elseif ($_ -eq "mingw64") { - $url = "$baseUrl/Toolchains%20targetting%20Win64/Personal%20Builds/mingw-builds/8.1.0/threads-posix/seh/x86_64-8.1.0-release-posix-seh-rt_v6-rev0.7z/download" - $sha256sum = '853970527b5de4a55ec8ca4d3fd732c00ae1c69974cc930c82604396d43e79f8' - } else { - throw "Unknown architecture $_" + $("mingw32", "mingw64") | ForEach-Object { + if ($_ -eq "mingw32") { + $url = "$baseUrl/Toolchains%20targetting%20Win32/Personal%20Builds/mingw-builds/8.1.0/threads-posix/dwarf/i686-8.1.0-release-posix-dwarf-rt_v6-rev0.7z/download" + $sha256sum = 'adb84b70094c0225dd30187ff995e311d19424b1eb8f60934c60e4903297f946' + } elseif ($_ -eq "mingw64") { + $url = "$baseUrl/Toolchains%20targetting%20Win64/Personal%20Builds/mingw-builds/8.1.0/threads-posix/seh/x86_64-8.1.0-release-posix-seh-rt_v6-rev0.7z/download" + $sha256sum = '853970527b5de4a55ec8ca4d3fd732c00ae1c69974cc930c82604396d43e79f8' + } else { + throw "Unknown architecture $_" + } + + $packagePath = Invoke-DownloadWithRetry $url + Test-FileChecksum -Path $packagePath -ExpectedSHA256Sum $sha256sum + Expand-7ZipArchive -Path $packagePath -DestinationPath "C:\" + + # Make a copy of mingw-make.exe to make.exe, which is a more discoverable name + # and so the same command line can be used on Windows as on macOS and Linux + $path = "C:\$_\bin\mingw32-make.exe" | Get-Item + Copy-Item -Path $path -Destination (Join-Path $path.Directory 'make.exe') + } + + Add-MachinePathItem "C:\mingw64\bin" + +} + +if (Test-IsWin22) { + # If Windows 2022, install version specified in the toolset + $version = (Get-ToolsetContent).mingw.version + $runtime = (Get-ToolsetContent).mingw.runtime + + $("mingw32", "mingw64") | ForEach-Object { + if ($_ -eq "mingw32") { + $arch = "i686" + $threads = "posix" + $exceptions = "dwarf" + } elseif ($_ -eq "mingw64") { + $arch = "x86_64" + $threads = "posix" + $exceptions = "seh" + } else { + throw "Unknown architecture $_" + } + + $url = Resolve-GithubReleaseAssetUrl ` + -Repo "niXman/mingw-builds-binaries" ` + -Version "$version" ` + -Asset "$arch-*-release-$threads-$exceptions-$runtime-*.7z" + + $packagePath = Invoke-DownloadWithRetry $url + Expand-7ZipArchive -Path $packagePath -DestinationPath "C:\" + + # Make a copy of mingw-make.exe to make.exe, which is a more discoverable name + # and so the same command line can be used on Windows as on macOS and Linux + $path = "C:\$_\bin\mingw32-make.exe" | Get-Item + Copy-Item -Path $path -Destination (Join-Path $path.Directory 'make.exe') } - $packagePath = Invoke-DownloadWithRetry $url - $hash = Get-FileHash -Path $packagePath -Algorithm SHA256 - if ($hash.Hash -ne $sha256sum) { - throw "Checksum verification failed for $packagePath" - } - Expand-7ZipArchive -Path $packagePath -DestinationPath "C:\" - - # Make a copy of mingw-make.exe to make.exe, which is a more discoverable name - # and so the same command line can be used on Windows as on macOS and Linux - $path = "C:\$_\bin\mingw32-make.exe" | Get-Item - Copy-Item -Path $path -Destination (Join-Path $path.Directory 'make.exe') - } - - Add-MachinePathItem "C:\mingw64\bin" - -} else { - $version = (Get-ToolsetContent).mingw.version - $runtime = (Get-ToolsetContent).mingw.runtime - - $("mingw32", "mingw64") | ForEach-Object { - if ($_ -eq "mingw32") { - $arch = "i686" - $threads = "posix" - $exceptions = "dwarf" - } elseif ($_ -eq "mingw64") { - $arch = "x86_64" - $threads = "posix" - $exceptions = "seh" - } else { - throw "Unknown architecture $_" - } - - $url = Resolve-GithubReleaseAssetUrl ` - -Repo "niXman/mingw-builds-binaries" ` - -Version "$version" ` - -Asset "$arch-*-release-$threads-$exceptions-$runtime-*.7z" - - $packagePath = Invoke-DownloadWithRetry $url - Expand-7ZipArchive -Path $packagePath -DestinationPath "C:\" - - # Make a copy of mingw-make.exe to make.exe, which is a more discoverable name - # and so the same command line can be used on Windows as on macOS and Linux - $path = "C:\$_\bin\mingw32-make.exe" | Get-Item - Copy-Item -Path $path -Destination (Join-Path $path.Directory 'make.exe') - } - - Add-MachinePathItem "C:\mingw64\bin" + Add-MachinePathItem "C:\mingw64\bin" } Invoke-PesterTests -TestFile "Tools" -TestName "Mingw64" diff --git a/images/windows/scripts/build/Install-Miniconda.ps1 b/images/windows/scripts/build/Install-Miniconda.ps1 index c628675cc..50382efd3 100644 --- a/images/windows/scripts/build/Install-Miniconda.ps1 +++ b/images/windows/scripts/build/Install-Miniconda.ps1 @@ -27,6 +27,6 @@ Install-Binary ` -InstallArgs @("/S", "/AddToPath=0", "/RegisterPython=0", "/D=$CondaDestination") ` -ExpectedSHA256Sum $distributorFileHash -[System.Environment]::SetEnvironmentVariable("CONDA", $CondaDestination, "Machine") +[Environment]::SetEnvironmentVariable("CONDA", $CondaDestination, "Machine") Invoke-PesterTests -TestFile "Miniconda" diff --git a/images/windows/scripts/build/Install-MongoDB.ps1 b/images/windows/scripts/build/Install-MongoDB.ps1 index da080a58b..e97257152 100644 --- a/images/windows/scripts/build/Install-MongoDB.ps1 +++ b/images/windows/scripts/build/Install-MongoDB.ps1 @@ -6,24 +6,24 @@ # Install mongodb package $toolsetVersion = (Get-ToolsetContent).mongodb.version -$getMongoReleases = Invoke-WebRequest -Uri "mongodb.com/docs/manual/release-notes/$toolsetVersion/" -UseBasicParsing +$getMongoReleases = Invoke-WebRequest -Uri "mongodb.com/docs/manual/release-notes/$toolsetVersion/" -UseBasicParsing $TargetReleases = $getMongoReleases.Links.href | Where-Object {$_ -like "#$toolsetVersion*---*"} $MinorVersions = @() foreach ($release in $TargetReleases) { if ($release -notlike "*upcoming*") { - $pattern = '\d+\.\d+\.\d+' - $version = $release | Select-String -Pattern $pattern -AllMatches | ForEach-Object { $_.Matches } | ForEach-Object { $_.Value } - $MinorVersions += $version + $pattern = '\d+\.\d+\.\d+' + $version = $release | Select-String -Pattern $pattern -AllMatches | ForEach-Object { $_.Matches } | ForEach-Object { $_.Value } + $MinorVersions += $version } - } +} $LatestVersion = $MinorVersions[0] Install-Binary ` - -Url "https://fastdl.mongodb.org/windows/mongodb-windows-x86_64-$LatestVersion-signed.msi" ` - -ExtraInstallArgs @('TARGETDIR=C:\PROGRA~1\MongoDB ADDLOCAL=ALL') ` - -ExpectedSignature (Get-ToolsetContent).mongodb.signature + -Url "https://fastdl.mongodb.org/windows/mongodb-windows-x86_64-$LatestVersion-signed.msi" ` + -ExtraInstallArgs @('TARGETDIR=C:\PROGRA~1\MongoDB ADDLOCAL=ALL') ` + -ExpectedSignature (Get-ToolsetContent).mongodb.signature # Add mongodb to the PATH $mongoPath = (Get-CimInstance Win32_Service -Filter "Name LIKE 'mongodb'").PathName diff --git a/images/windows/scripts/build/Install-Msys2.ps1 b/images/windows/scripts/build/Install-Msys2.ps1 index 45e0793f1..5b167e40d 100644 --- a/images/windows/scripts/build/Install-Msys2.ps1 +++ b/images/windows/scripts/build/Install-Msys2.ps1 @@ -11,25 +11,27 @@ $dash = "-" * 40 $origPath = $env:PATH function Install-Msys2 { - $msys2_release = "https://api.github.com/repos/msys2/msys2-installer/releases/latest" - $assets = (Invoke-RestMethod -Uri $msys2_release).assets - $msys2Uri = ($assets | Where-Object { $_.name -match "^msys2-x86_64" -and $_.name.EndsWith(".exe") }).browser_download_url + # We can't use Resolve-GithubReleaseAssetUrl function here + # because msys2-installer releases don't have a consistent versioning scheme + + $assets = (Invoke-RestMethod -Uri "https://api.github.com/repos/msys2/msys2-installer/releases/latest").assets + $downloadUri = ($assets | Where-Object { $_.name -match "^msys2-x86_64" -and $_.name.EndsWith(".exe") }).browser_download_url + $installerName = Split-Path $downloadUri -Leaf # Download the latest msys2 x86_64, filename includes release date - Write-Host "Starting msys2 download using $($msys2Uri.split('/')[-1])" - $msys2File = Invoke-DownloadWithRetry $msys2Uri - Write-Host "Finished download" + Write-Host "Download msys2 installer $installerName" + $installerPath = Invoke-DownloadWithRetry $downloadUri - #region Supply chain security - Kind - $hashUrl = ($assets.browser_download_url -match "msys2-checksums.txt") | Select-Object -First 1 - $externalHash = (Invoke-RestMethod -Uri $hashURL).ToString().Split("`n").Where({ $_ -ilike "*msys2-x86_64*" }).Split(' ')[0] - Test-FileChecksum $msys2File -ExpectedSHA256Sum $externalHash + #region Supply chain security - MSYS2 + $externalHash = Get-ChecksumFromUrl -Type "SHA256" ` + -Url ($downloadUri -replace $installerName, "msys2-checksums.txt") ` + -FileName $installerName + Test-FileChecksum $installerPath -ExpectedSHA256Sum $externalHash #endregion - # extract tar.xz to C:\ Write-Host "Starting msys2 installation" - & $msys2File in --confirm-command --accept-messages --root C:/msys64 - Remove-Item $msys2File + & $installerPath in --confirm-command --accept-messages --root C:/msys64 + Remove-Item $installerPath } function Install-Msys2Packages($Packages) { diff --git a/images/windows/scripts/build/Install-MysqlCli.ps1 b/images/windows/scripts/build/Install-MysqlCli.ps1 index 7ca934dca..e04f12399 100644 --- a/images/windows/scripts/build/Install-MysqlCli.ps1 +++ b/images/windows/scripts/build/Install-MysqlCli.ps1 @@ -10,12 +10,14 @@ Install-Binary ` -ExpectedSignature '3BDA323E552DB1FDE5F4FBEE75D6D5B2B187EEDC' # Downloading mysql -[version]$MysqlVersion = (Get-ToolsetContent).mysql.version +[version] $MysqlVersion = (Get-ToolsetContent).mysql.version $MysqlVersionMajorMinor = $MysqlVersion.ToString(2) if ($MysqlVersion.Build -lt 0) { - $MysqlVersion = (Invoke-RestMethod -Uri "https://dev.mysql.com/downloads/mysql/${MysqlVersionMajorMinor}.html" -Headers @{ 'User-Agent' = 'curl/8.4.0' } | - Select-String -Pattern "${MysqlVersionMajorMinor}\.\d+").Matches.Value + $downloadsPageUrl = "https://dev.mysql.com/downloads/mysql/${MysqlVersionMajorMinor}.html" + $MysqlVersion = Invoke-RestMethod -Uri $downloadsPageUrl -Headers @{ 'User-Agent' = 'curl/8.4.0' } ` + | Select-String -Pattern "${MysqlVersionMajorMinor}\.\d+" ` + | ForEach-Object { $_.Matches.Value } } $MysqlVersionFull = $MysqlVersion.ToString() diff --git a/images/windows/scripts/build/Install-Pipx.ps1 b/images/windows/scripts/build/Install-Pipx.ps1 index fb86d7166..64018fbb9 100644 --- a/images/windows/scripts/build/Install-Pipx.ps1 +++ b/images/windows/scripts/build/Install-Pipx.ps1 @@ -10,8 +10,8 @@ $env:PIPX_HOME = "${env:ProgramFiles(x86)}\pipx" pip install pipx Add-MachinePathItem "${env:PIPX_BIN_DIR}" -[System.Environment]::SetEnvironmentVariable("PIPX_BIN_DIR", $env:PIPX_BIN_DIR, "Machine") -[System.Environment]::SetEnvironmentVariable("PIPX_HOME", $env:PIPX_HOME, "Machine") +[Environment]::SetEnvironmentVariable("PIPX_BIN_DIR", $env:PIPX_BIN_DIR, "Machine") +[Environment]::SetEnvironmentVariable("PIPX_HOME", $env:PIPX_HOME, "Machine") Invoke-PesterTests -TestFile "Tools" -TestName "Pipx" diff --git a/images/windows/scripts/build/Install-PostgreSQL.ps1 b/images/windows/scripts/build/Install-PostgreSQL.ps1 index 94ca97036..e9223fae9 100644 --- a/images/windows/scripts/build/Install-PostgreSQL.ps1 +++ b/images/windows/scripts/build/Install-PostgreSQL.ps1 @@ -3,20 +3,20 @@ $pgUser = "postgres" $pgPwd = "root" # Prepare environment variable for validation -[System.Environment]::SetEnvironmentVariable("PGUSER", $pgUser, "Machine") -[System.Environment]::SetEnvironmentVariable("PGPASSWORD", $pgPwd, "Machine") +[Environment]::SetEnvironmentVariable("PGUSER", $pgUser, "Machine") +[Environment]::SetEnvironmentVariable("PGPASSWORD", $pgPwd, "Machine") # Define latest available version to install based on version specified in the toolset $toolsetVersion = (Get-ToolsetContent).postgresql.version $getPostgreReleases = Invoke-WebRequest -Uri "https://git.postgresql.org/gitweb/?p=postgresql.git;a=tags" -UseBasicParsing # Getting all links matched to the pattern (e.g.a=log;h=refs/tags/REL_14) $TargetReleases = $getPostgreReleases.Links.href | Where-Object { $_ -match "a=log;h=refs/tags/REL_$toolsetVersion" } -[Int32]$OutNumber = $null +[Int32] $OutNumber = $null $MinorVersions = @() foreach ($release in $TargetReleases) { $version = $release.split('/')[-1] # Checking if the latest symbol of the release version is actually a number. If yes, add to $MinorVersions array - if ([Int32]::TryParse($($version.Split('_')[-1]), [ref]$OutNumber)) { + if ([Int32]::TryParse($($version.Split('_')[-1]), [ref] $OutNumber)) { $MinorVersions += $OutNumber } } @@ -72,9 +72,9 @@ if ($exitCode -ne 0) { } # Added PostgreSQL environment variable -[System.Environment]::SetEnvironmentVariable("PGBIN", $pgBin, "Machine") -[System.Environment]::SetEnvironmentVariable("PGROOT", $pgRoot, "Machine") -[System.Environment]::SetEnvironmentVariable("PGDATA", $pgData, "Machine") +[Environment]::SetEnvironmentVariable("PGBIN", $pgBin, "Machine") +[Environment]::SetEnvironmentVariable("PGROOT", $pgRoot, "Machine") +[Environment]::SetEnvironmentVariable("PGDATA", $pgData, "Machine") # Stop and disable PostgreSQL service $pgService = Get-Service -Name postgresql* diff --git a/images/windows/scripts/build/Install-PowerShellModules.ps1 b/images/windows/scripts/build/Install-PowerShellModules.ps1 index 130c63db6..2017e9869 100644 --- a/images/windows/scripts/build/Install-PowerShellModules.ps1 +++ b/images/windows/scripts/build/Install-PowerShellModules.ps1 @@ -9,22 +9,18 @@ # Install PowerShell modules $modules = (Get-ToolsetContent).powershellModules -foreach($module in $modules) -{ +foreach ($module in $modules) { $moduleName = $module.name Write-Host "Installing ${moduleName} module" - if ($module.versions) - { - foreach ($version in $module.versions) - { + if ($module.versions) { + foreach ($version in $module.versions) { Write-Host " - $version" Install-Module -Name $moduleName -RequiredVersion $version -Scope AllUsers -SkipPublisherCheck -Force } - continue + } else { + Install-Module -Name $moduleName -Scope AllUsers -SkipPublisherCheck -Force } - - Install-Module -Name $moduleName -Scope AllUsers -SkipPublisherCheck -Force } Import-Module Pester diff --git a/images/windows/scripts/build/Install-PowershellCore.ps1 b/images/windows/scripts/build/Install-PowershellCore.ps1 index e168a998f..d56c9bf30 100644 --- a/images/windows/scripts/build/Install-PowershellCore.ps1 +++ b/images/windows/scripts/build/Install-PowershellCore.ps1 @@ -16,10 +16,11 @@ try { $release = $metadata.LTSReleaseTag[0] -replace '^v' $downloadUrl = "https://github.com/PowerShell/PowerShell/releases/download/v${release}/PowerShell-${release}-win-x64.msi" - $hashUrl = "https://github.com/PowerShell/PowerShell/releases/download/v${release}/hashes.sha256" - $expectedSHA256Sum = (Invoke-RestMethod -Uri $hashURL).ToString().Split("`n").Where({ $_ -ilike "*PowerShell-${Release}-win-x64.msi*" }).Split(' ')[0] - - Install-Binary -Url $downloadUrl -ExpectedSHA256Sum $expectedSHA256Sum + $installerName = Split-Path $downloadUrl -Leaf + $externalHash = Get-ChecksumFromUrl -Type "SHA256" ` + -Url ($downloadUrl -replace $installerName, "hashes.sha256") ` + -FileName $installerName + Install-Binary -Url $downloadUrl -ExpectedSHA256Sum $externalHash } finally { # Restore original value [Net.ServicePointManager]::SecurityProtocol = $originalValue @@ -30,6 +31,6 @@ try { # While the update check happens during the first session in a given 24-hour period, for performance reasons, # the notification will only be shown on the start of subsequent sessions. # Also for performance reasons, the check will not start until at least 3 seconds after the session begins. -[System.Environment]::SetEnvironmentVariable("POWERSHELL_UPDATECHECK", "Off", [System.EnvironmentVariableTarget]::Machine) +[Environment]::SetEnvironmentVariable("POWERSHELL_UPDATECHECK", "Off", "Machine") Invoke-PesterTests -TestFile "Tools" -TestName "PowerShell Core" diff --git a/images/windows/scripts/build/Install-RootCA.ps1 b/images/windows/scripts/build/Install-RootCA.ps1 index 5b618b450..d536fe9e8 100644 --- a/images/windows/scripts/build/Install-RootCA.ps1 +++ b/images/windows/scripts/build/Install-RootCA.ps1 @@ -33,7 +33,7 @@ function Get-CertificatesWithoutPropId { $certs | ForEach-Object -Process { $certHandle = $_.Handle $isPropertySet = [PKI.Cert]::CertGetCertificateContextProperty( - $certHandle, $CERT_NOT_BEFORE_FILETIME_PROP_ID, $null, [ref]$null + $certHandle, $CERT_NOT_BEFORE_FILETIME_PROP_ID, $null, [ref] $null ) if (-not $isPropertySet) { Write-Host "Subject: $($_.Subject)" @@ -43,31 +43,17 @@ function Get-CertificatesWithoutPropId { $certsWithoutPropId } -function Invoke-WithRetry { - <# - .SYNOPSIS - Runs $command block until $BreakCondition or $RetryCount is reached. - #> - - param([ScriptBlock]$Command, [ScriptBlock] $BreakCondition, [int] $RetryCount=5, [int] $Sleep=10) - - $c = 0 - while($c -lt $RetryCount){ - $result = & $Command - if(& $BreakCondition){ - break - } - Start-Sleep $Sleep - $c++ - } - $result -} - function Import-SSTFromWU { # Serialized Certificate Store File $sstFile = "$env:TEMP\roots.sst" # Generate SST from Windows Update - $result = Invoke-WithRetry { certutil.exe -generateSSTFromWU $sstFile } {$LASTEXITCODE -eq 0} + $result = Invoke-ScriptBlockWithRetry -RetryCount 5 -RetryIntervalSeconds 10 -Command { + $r = certutil.exe -generateSSTFromWU $sstFile + if ($LASTEXITCODE -ne 0) { + throw "failed to generate $sstFile sst file`n$o" + } + return $r + } if ($LASTEXITCODE -ne 0) { Write-Host "[Error]: failed to generate $sstFile sst file`n$result" exit $LASTEXITCODE @@ -88,7 +74,9 @@ function Import-SSTFromWU { } function Clear-CertificatesPropId { - param([hashtable]$CertsWithoutPropId) + param( + [hashtable] $CertsWithoutPropId + ) # List installed certificates $certs = Get-ChildItem -Path Cert:\LocalMachine\Root diff --git a/images/windows/scripts/build/Install-Ruby.ps1 b/images/windows/scripts/build/Install-Ruby.ps1 index dc49875fc..01bd525bb 100644 --- a/images/windows/scripts/build/Install-Ruby.ps1 +++ b/images/windows/scripts/build/Install-Ruby.ps1 @@ -3,39 +3,12 @@ ## Desc: Install Ruby using the RubyInstaller2 package and set the default Ruby version ################################################################################ -function Get-RubyVersions { - param ( - [System.String] $Arch = "x64", - [System.String] $Extension = "7z", - [System.String] $ReleasesAmount = "50" - ) - - $uri = "https://api.github.com/repos/oneclick/rubyinstaller2/releases?per_page=$ReleasesAmount" - try { - $versionLists = @{} - $assets = (Invoke-RestMethod -Uri $uri).Where{ -not $_.prerelease }.assets - $7zArchives = $assets.Where{ $_.name.EndsWith("$Arch.$Extension") } - $majorMinorGroups = $7zArchives | Group-Object { $_.name.Replace("rubyinstaller-", "").Substring(0, 3) } - foreach ($majorMinorGroup in $majorMinorGroups) { - $group = $majorMinorGroup.Group - $sortVersions = $group | Sort-Object { [Version]$_.name.Replace("rubyinstaller-", "").Replace("-$Arch.$Extension", "").Replace("-", ".") } - $latestVersion = $sortVersions | Select-Object -Last 1 - $versionLists[$majorMinorGroup.Name] = $latestVersion.browser_download_url - - } - return $versionLists - } catch { - Write-Host "Unable to send request to the '$uri'. Error: '$_'" - exit 1 - } -} - # Most of this logic is from # https://github.com/ruby/setup-ruby/blob/master/windows.js function Install-Ruby { param( - [String]$PackagePath, - [String]$Architecture = "x64" + [String] $PackagePath, + [String] $Architecture = "x64" ) # Create Ruby toolcache folder @@ -44,47 +17,46 @@ function Install-Ruby { Write-Host "Creating Ruby toolcache folder" New-Item -ItemType Directory -Path $rubyToolcachePath | Out-Null } - - # Expand archive with binaries + $packageName = [IO.Path]::GetFileNameWithoutExtension((Split-Path -Path $PackagePath -Leaf)) + Write-Host "Expanding Ruby archive $packageName" $tempFolder = Join-Path -Path $rubyToolcachePath -ChildPath $packageName Expand-7ZipArchive -Path $PackagePath -DestinationPath $rubyToolcachePath # Get Ruby version from binaries $rubyVersion = & "$tempFolder\bin\ruby.exe" -e "print RUBY_VERSION" - - if ($rubyVersion) { - Write-Host "Installing Ruby $rubyVersion" - $rubyVersionPath = Join-Path -Path $rubyToolcachePath -ChildPath $rubyVersion - $rubyArchPath = Join-Path -Path $rubyVersionPath -ChildPath $Architecture - - Write-Host "Creating Ruby '${rubyVersion}' folder in '${rubyVersionPath}'" - New-Item -ItemType Directory -Path $rubyVersionPath -Force | Out-Null - - Write-Host "Moving Ruby '${rubyVersion}' files to '${rubyArchPath}'" - Invoke-ScriptBlockWithRetry -Command { - Move-Item -Path $tempFolder -Destination $rubyArchPath -ErrorAction Stop | Out-Null - } - - Write-Host "Removing Ruby '${rubyVersion}' documentation '${rubyArchPath}\share\doc' folder" - Remove-Item -Path "${rubyArchPath}\share\doc" -Force -Recurse -ErrorAction Ignore - - Write-Host "Creating complete file" - New-Item -ItemType File -Path $rubyVersionPath -Name "$Architecture.complete" | Out-Null - } else { - Write-Host "Ruby application is not found. Failed to expand '$PackagePath' archive" - exit 1 + if (($LASTEXITCODE -ne 0) -or (-not $rubyVersion)) { + throw "Unable to determine Ruby version. Exit code: $LASTEXITCODE, output: '$rubyVersion'" } + Write-Host "Ruby version is $rubyVersion" + + $rubyVersionPath = Join-Path -Path $rubyToolcachePath -ChildPath $rubyVersion + $rubyArchPath = Join-Path -Path $rubyVersionPath -ChildPath $Architecture + + Write-Host "Creating Ruby '${rubyVersion}' folder in '${rubyVersionPath}'" + New-Item -ItemType Directory -Path $rubyVersionPath -Force | Out-Null + + Write-Host "Moving Ruby '${rubyVersion}' files to '${rubyArchPath}'" + Invoke-ScriptBlockWithRetry -Command { + Move-Item -Path $tempFolder -Destination $rubyArchPath -ErrorAction Stop | Out-Null + } + + Write-Host "Removing Ruby '${rubyVersion}' documentation '${rubyArchPath}\share\doc' folder" + Remove-Item -Path "${rubyArchPath}\share\doc" -Force -Recurse -ErrorAction Ignore + + Write-Host "Creating complete file for Ruby $rubyVersion $Architecture" + New-Item -ItemType File -Path $rubyVersionPath -Name "$Architecture.complete" | Out-Null } function Set-DefaultRubyVersion { param( [Parameter(Mandatory = $true)] - [System.Version] $Version, - [System.String] $Arch = "x64" + [version] $Version, + [Alias("Arch")] + [string] $Architecture = "x64" ) - $rubyPath = Join-Path $env:AGENT_TOOLSDIRECTORY "/Ruby/${Version}*/${Arch}/bin" + $rubyPath = Join-Path $env:AGENT_TOOLSDIRECTORY "/Ruby/${Version}*/${Architecture}/bin" $rubyDir = (Get-Item -Path $rubyPath).FullName Write-Host "Use Ruby ${Version} as a system Ruby" @@ -95,21 +67,15 @@ function Set-DefaultRubyVersion { $rubyTools = (Get-ToolsetContent).toolcache | Where-Object { $_.name -eq "Ruby" } $rubyToolVersions = $rubyTools.versions -# Get Ruby versions from the repo -$rubyLatestMajorVersions = Get-RubyVersions - Write-Host "Starting installation Ruby..." foreach ($rubyVersion in $rubyToolVersions) { Write-Host "Starting Ruby $rubyVersion installation" - # Get url for the latest major Ruby version - $url = $rubyLatestMajorVersions[$rubyVersion] - if ($url) { - $tempRubyPackagePath = Invoke-DownloadWithRetry $url - Install-Ruby -PackagePath $tempRubyPackagePath - } else { - Write-Host "Url not found for the '$rubyVersion' version" - exit 1 - } + $downloadUrl = Resolve-GithubReleaseAssetUrl ` + -Repo "oneclick/rubyinstaller2" ` + -Version "$rubyVersion*" ` + -UrlMatchPattern "*-x64.7z" + $packagePath = Invoke-DownloadWithRetry $downloadUrl + Install-Ruby -PackagePath $packagePath } Set-DefaultRubyVersion -Version $rubyTools.default -Arch $rubyTools.arch diff --git a/images/windows/scripts/build/Install-Runner.ps1 b/images/windows/scripts/build/Install-Runner.ps1 index e3f42cdfc..dc7a6f7f3 100644 --- a/images/windows/scripts/build/Install-Runner.ps1 +++ b/images/windows/scripts/build/Install-Runner.ps1 @@ -5,10 +5,11 @@ ################################################################################ Write-Host "Download latest Runner for GitHub Actions" -$release = Invoke-RestMethod -Uri "https://api.github.com/repos/actions/runner/releases/latest" -$version = $release.tag_name.Trim("v") -$downloadUrl = ($release.assets.browser_download_url -ilike "*actions-runner-win-x64-${version}.zip*") | Select-Object -First 1 -$fileName = [System.IO.Path]::GetFileName($downloadUrl) +$downloadUrl = Resolve-GithubReleaseAssetUrl ` + -Repo "actions/runner" ` + -Version "latest" ` + -UrlMatchPattern "actions-runner-win-x64-*[0-9.].zip" +$fileName = Split-Path $downloadUrl -Leaf New-Item -Path "C:\ProgramData\runner" -ItemType Directory Invoke-DownloadWithRetry -Url $downloadUrl -Path "C:\ProgramData\runner\$fileName" diff --git a/images/windows/scripts/build/Install-Rust.ps1 b/images/windows/scripts/build/Install-Rust.ps1 index e8d19f406..b254f9a07 100644 --- a/images/windows/scripts/build/Install-Rust.ps1 +++ b/images/windows/scripts/build/Install-Rust.ps1 @@ -14,7 +14,7 @@ $rustupPath = Invoke-DownloadWithRetry "https://static.rust-lang.org/rustup/dist #region Supply chain security $distributorFileHash = (Invoke-RestMethod -Uri 'https://static.rust-lang.org/rustup/dist/x86_64-pc-windows-msvc/rustup-init.exe.sha256').Trim() -Test-FileChecksum (Join-Path ${env:TEMP} 'rustup-init.exe') -ExpectedSHA256Sum $distributorFileHash +Test-FileChecksum $rustupPath -ExpectedSHA256Sum $distributorFileHash #endregion # Install Rust by running rustup-init.exe (disabling the confirmation prompt with -y) diff --git a/images/windows/scripts/build/Install-SQLPowerShellTools.ps1 b/images/windows/scripts/build/Install-SQLPowerShellTools.ps1 index a12c86ffe..40b407338 100644 --- a/images/windows/scripts/build/Install-SQLPowerShellTools.ps1 +++ b/images/windows/scripts/build/Install-SQLPowerShellTools.ps1 @@ -8,13 +8,13 @@ $signatureThumbrint = "9ACA9419E53D3C9E56396DD2335FF683A8B0B8F3" # install required MSIs Install-Binary ` - -Url "${baseUrl}/SQLSysClrTypes.msi" ` - -ExpectedSignature $signatureThumbrint + -Url "${baseUrl}/SQLSysClrTypes.msi" ` + -ExpectedSignature $signatureThumbrint Install-Binary ` - -Url "${baseUrl}/SharedManagementObjects.msi" ` - -ExpectedSignature $signatureThumbrint + -Url "${baseUrl}/SharedManagementObjects.msi" ` + -ExpectedSignature $signatureThumbrint Install-Binary ` - -Url "${baseUrl}/PowerShellTools.msi" ` - -ExpectedSignature $signatureThumbrint + -Url "${baseUrl}/PowerShellTools.msi" ` + -ExpectedSignature $signatureThumbrint diff --git a/images/windows/scripts/build/Install-ServiceFabricSDK.ps1 b/images/windows/scripts/build/Install-ServiceFabricSDK.ps1 index 18cb6461d..4d6236181 100644 --- a/images/windows/scripts/build/Install-ServiceFabricSDK.ps1 +++ b/images/windows/scripts/build/Install-ServiceFabricSDK.ps1 @@ -14,13 +14,13 @@ $urlBase = "https://download.microsoft.com/download/b/8/a/b8a2fb98-0ec1-41e5-be9 # Install Service Fabric Runtime for Windows Install-Binary ` - -Url "${urlBase}/MicrosoftServiceFabric.${runtimeVersion}.exe" ` - -InstallArgs @("/accepteula ", "/quiet", "/force") ` - -ExpectedSignature (Get-ToolsetContent).serviceFabric.runtime.signature + -Url "${urlBase}/MicrosoftServiceFabric.${runtimeVersion}.exe" ` + -InstallArgs @("/accepteula ", "/quiet", "/force") ` + -ExpectedSignature (Get-ToolsetContent).serviceFabric.runtime.signature # Install Service Fabric SDK Install-Binary ` - -Url "${urlBase}/MicrosoftServiceFabricSDK.${sdkVersion}.msi" ` - -ExpectedSignature (Get-ToolsetContent).serviceFabric.sdk.signature + -Url "${urlBase}/MicrosoftServiceFabricSDK.${sdkVersion}.msi" ` + -ExpectedSignature (Get-ToolsetContent).serviceFabric.sdk.signature Invoke-PesterTests -TestFile "Tools" -TestName "ServiceFabricSDK" diff --git a/images/windows/scripts/build/Install-Stack.ps1 b/images/windows/scripts/build/Install-Stack.ps1 index 5c5180407..3e4ad3fc9 100644 --- a/images/windows/scripts/build/Install-Stack.ps1 +++ b/images/windows/scripts/build/Install-Stack.ps1 @@ -5,19 +5,23 @@ ################################################################################ Write-Host "Get the latest Stack version..." -$StackReleasesJson = Invoke-RestMethod "https://api.github.com/repos/commercialhaskell/stack/releases/latest" -$Version = $StackReleasesJson.name.TrimStart("v") -$DownloadFilePattern = "windows-x86_64.zip" -$DownloadUrl = $StackReleasesJson.assets | Where-Object { $_.name.EndsWith($DownloadFilePattern) } | Select-Object -ExpandProperty "browser_download_url" -First 1 + +$version = (Get-GithubReleasesByVersion -Repo "commercialhaskell/stack" -Version "latest" -WithAssetsOnly).version + +$downloadUrl = Resolve-GithubReleaseAssetUrl ` + -Repo "commercialhaskell/stack" ` + -Version $version ` + -UrlMatchPattern "stack-*-windows-x86_64.zip" Write-Host "Download stack archive" -$StackToolcachePath = Join-Path $Env:AGENT_TOOLSDIRECTORY "stack\$Version" +$StackToolcachePath = Join-Path $Env:AGENT_TOOLSDIRECTORY "stack\$version" $DestinationPath = Join-Path $StackToolcachePath "x64" -$StackArchivePath = Invoke-DownloadWithRetry $DownloadUrl +$StackArchivePath = Invoke-DownloadWithRetry $downloadUrl #region Supply chain security - Stack -$hashUrl = $StackReleasesJson.assets | Where-Object { $_.name.EndsWith("$DownloadFilePattern.sha256") } | Select-Object -ExpandProperty "browser_download_url" -First 1 -$externalHash = (Invoke-RestMethod -Uri $hashURL).ToString().Split("`n").Where({ $_ -ilike "*$DownloadFilePattern*" }).Split(' ')[0] +$externalHash = Get-ChecksumFromUrl -Type "SHA256" ` + -Url "$downloadUrl.sha256" ` + -FileName (Split-Path $downloadUrl -Leaf) Test-FileChecksum $StackArchivePath -ExpectedSHA256Sum $externalHash #endregion diff --git a/images/windows/scripts/build/Install-Toolset.ps1 b/images/windows/scripts/build/Install-Toolset.ps1 index 19c1f8b4f..7993b0976 100644 --- a/images/windows/scripts/build/Install-Toolset.ps1 +++ b/images/windows/scripts/build/Install-Toolset.ps1 @@ -51,12 +51,11 @@ foreach ($tool in $tools) { | Where-Object { ($_.platform -eq $tool.platform) -and ($_.arch -eq $tool.arch) -and ($_.toolset -eq $tool.toolset) } ` | Select-Object -First 1 - Write-Host "Installing $($tool.name) $toolVersion $($tool.arch)..." - if ($null -ne $asset) { - Install-Asset -ReleaseAsset $asset - } else { - Write-Host "Asset was not found in versions manifest" - exit 1 + if (-not $asset) { + throw "Asset for $($tool.name) $toolVersion $($tool.arch) not found in versions manifest" } + + Write-Host "Installing $($tool.name) $toolVersion $($tool.arch)..." + Install-Asset -ReleaseAsset $asset } } diff --git a/images/windows/scripts/build/Install-VCRedist.ps1 b/images/windows/scripts/build/Install-VCRedist.ps1 index fea415cf7..d6aa737ed 100644 --- a/images/windows/scripts/build/Install-VCRedist.ps1 +++ b/images/windows/scripts/build/Install-VCRedist.ps1 @@ -8,13 +8,13 @@ $argumentList = ("/install", "/quiet", "/norestart") $signatureThumbrint = "ABDCA79AF9DD48A0EA702AD45260B3C03093FB4B" Install-Binary ` - -Url "${baseUrl}/vcredist_x86.exe" ` - -InstallArgs $argumentList ` - -ExpectedSignature $signatureThumbrint + -Url "${baseUrl}/vcredist_x86.exe" ` + -InstallArgs $argumentList ` + -ExpectedSignature $signatureThumbrint Install-Binary ` - -Url "${baseUrl}/vcredist_x64.exe" ` - -InstallArgs $argumentList ` - -ExpectedSignature $signatureThumbrint + -Url "${baseUrl}/vcredist_x64.exe" ` + -InstallArgs $argumentList ` + -ExpectedSignature $signatureThumbrint Invoke-PesterTests -TestFile "Tools" -TestName "VCRedist" diff --git a/images/windows/scripts/build/Install-VisualStudio.ps1 b/images/windows/scripts/build/Install-VisualStudio.ps1 index 148e6f84d..73b97ddf7 100644 --- a/images/windows/scripts/build/Install-VisualStudio.ps1 +++ b/images/windows/scripts/build/Install-VisualStudio.ps1 @@ -3,16 +3,16 @@ ## Desc: Install Visual Studio ################################################################################ -$toolset = Get-ToolsetContent +$vsToolset = (Get-ToolsetContent).visualStudio # Install VS Install-VisualStudio ` - -Version $toolset.visualStudio.subversion ` - -Edition $toolset.visualStudio.edition ` - -Channel $toolset.visualStudio.channel ` - -RequiredComponents $toolset.visualStudio.workloads ` + -Version $vsToolset.subversion ` + -Edition $vsToolset.edition ` + -Channel $vsToolset.channel ` + -RequiredComponents $vsToolset.workloads ` -ExtraArgs "--allWorkloads --includeRecommended --remove Component.CPython3.x64" ` - -SignatureThumbprint $toolset.visualStudio.signature + -SignatureThumbprint $vsToolset.signature # Find the version of VS installed for this instance # Only supports a single instance @@ -20,8 +20,7 @@ $vsProgramData = Get-Item -Path "C:\ProgramData\Microsoft\VisualStudio\Packages\ $instanceFolders = Get-ChildItem -Path $vsProgramData.FullName if ($instanceFolders -is [array]) { - Write-Host "More than one instance installed" - exit 1 + throw "More than one instance installed" } # Updating content of MachineState.json file to disable autoupdate of VSIX extensions @@ -35,7 +34,7 @@ if (Test-IsWin19) { -Url 'https://go.microsoft.com/fwlink/p/?LinkId=838916' ` -InstallArgs @("/q", "/norestart", "/ceip off", "/features OptionId.WindowsSoftwareDevelopmentKit") ` -ExpectedSignature 'C91545B333C52C4465DE8B90A3FAF4E1D9C58DFA' - + # Install Windows 11 SDK version 10.0.22621.0 Install-Binary -Type EXE ` -Url 'https://go.microsoft.com/fwlink/p/?linkid=2196241' ` diff --git a/images/windows/scripts/build/Install-WebPlatformInstaller.ps1 b/images/windows/scripts/build/Install-WebPlatformInstaller.ps1 index 7fc802644..5c7c6fb7d 100644 --- a/images/windows/scripts/build/Install-WebPlatformInstaller.ps1 +++ b/images/windows/scripts/build/Install-WebPlatformInstaller.ps1 @@ -4,7 +4,7 @@ ################################################################################ Install-Binary -Type MSI ` - -Url 'http://go.microsoft.com/fwlink/?LinkId=287166' ` - -ExpectedSignature 'C3A3D43788E7ABCD287CB4F5B6583043774F99D2' + -Url 'http://go.microsoft.com/fwlink/?LinkId=287166' ` + -ExpectedSignature 'C3A3D43788E7ABCD287CB4F5B6583043774F99D2' Invoke-PesterTests -TestFile "Tools" -TestName "WebPlatformInstaller" diff --git a/images/windows/scripts/build/Install-WinAppDriver.ps1 b/images/windows/scripts/build/Install-WinAppDriver.ps1 index 192b7004f..e4a6a8264 100644 --- a/images/windows/scripts/build/Install-WinAppDriver.ps1 +++ b/images/windows/scripts/build/Install-WinAppDriver.ps1 @@ -4,12 +4,13 @@ #################################################################################### [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 - -$latestReleaseUrl = 'https://api.github.com/repos/microsoft/WinAppDriver/releases/latest' -$installerUrl = (Invoke-RestMethod -Uri $latestReleaseUrl).assets.browser_download_url +$downloadUrl = Resolve-GithubReleaseAssetUrl ` + -Repo "microsoft/WinAppDriver" ` + -Version "latest" ` + -UrlMatchPattern "WindowsApplicationDriver_*.msi" Install-Binary ` - -Url $installerUrl ` - -ExpectedSignature '2485A7AFA98E178CB8F30C9838346B514AEA4769' + -Url $downloadUrl ` + -ExpectedSignature '2485A7AFA98E178CB8F30C9838346B514AEA4769' Invoke-PesterTests -TestFile "WinAppDriver" -TestName "WinAppDriver" diff --git a/images/windows/scripts/build/Install-WindowsFeatures.ps1 b/images/windows/scripts/build/Install-WindowsFeatures.ps1 index b4680242e..c85d25bc7 100644 --- a/images/windows/scripts/build/Install-WindowsFeatures.ps1 +++ b/images/windows/scripts/build/Install-WindowsFeatures.ps1 @@ -1,3 +1,8 @@ +#################################################################################### +## File: Install-WindowsFeatures.ps1 +## Desc: Install Windows Features +#################################################################################### + $windowsFeatures = (Get-ToolsetContent).windowsFeatures foreach ($feature in $windowsFeatures) { diff --git a/images/windows/scripts/build/Install-WindowsUpdates.ps1 b/images/windows/scripts/build/Install-WindowsUpdates.ps1 index b9da01836..681af89c3 100644 --- a/images/windows/scripts/build/Install-WindowsUpdates.ps1 +++ b/images/windows/scripts/build/Install-WindowsUpdates.ps1 @@ -28,8 +28,7 @@ function Install-WindowsUpdates { $failedUpdates = $updates[0] | Where-Object Title -notmatch "Microsoft Defender Antivirus" | Where-Object { -not ($notFailedUpdateNames -match $_.KB) } if ( $failedUpdates ) { - Write-Host "Windows updates failed to install: $($failedUpdates.KB)" - exit 1 + throw "Windows updates failed to install: $($failedUpdates.KB)" } } diff --git a/images/windows/scripts/build/Install-Zstd.ps1 b/images/windows/scripts/build/Install-Zstd.ps1 index 2e8ce0174..20ae3fd2f 100644 --- a/images/windows/scripts/build/Install-Zstd.ps1 +++ b/images/windows/scripts/build/Install-Zstd.ps1 @@ -3,17 +3,18 @@ ## Desc: Install zstd ################################################################################ -$url = "https://api.github.com/repos/facebook/zstd/releases/latest" -# Explicitly set type to string since match returns array by default -[System.String] $zstdLatest = (Invoke-RestMethod -Uri $url).assets.browser_download_url -match "zstd-.+-win64.zip$" -$zstdArchivePath = Invoke-DownloadWithRetry $zstdLatest +$downloadUrl = Resolve-GithubReleaseAssetUrl ` + -Repo "facebook/zstd" ` + -Version "latest" ` + -UrlMatchPattern "zstd-*-win64.zip" +$zstdArchivePath = Invoke-DownloadWithRetry $downloadUrl +$zstdName = [IO.Path]::GetFileNameWithoutExtension($zstdArchivePath) $toolPath = "C:\tools" $zstdPath = Join-Path $toolPath zstd -$zstdParentName = [IO.Path]::GetFileNameWithoutExtension($zstdLatest) $filesInArchive = 7z l $zstdArchivePath | Out-String -if ($filesInArchive.Contains($zstdParentName)) { +if ($filesInArchive.Contains($zstdName)) { Expand-7ZipArchive -Path $zstdArchivePath -DestinationPath $toolPath Invoke-ScriptBlockWithRetry -Command { Move-Item -Path "${zstdPath}*" -Destination $zstdPath -ErrorAction Stop diff --git a/images/windows/scripts/docs-gen/SoftwareReport.CachedTools.psm1 b/images/windows/scripts/docs-gen/SoftwareReport.CachedTools.psm1 index d76837730..a62ed4525 100644 --- a/images/windows/scripts/docs-gen/SoftwareReport.CachedTools.psm1 +++ b/images/windows/scripts/docs-gen/SoftwareReport.CachedTools.psm1 @@ -1,28 +1,28 @@ function Get-ToolcacheGoVersions { $toolcachePath = Join-Path $env:AGENT_TOOLSDIRECTORY "Go" - return Get-ChildItem $toolcachePath -Name | Sort-Object { [Version]$_ } + return Get-ChildItem $toolcachePath -Name | Sort-Object { [Version] $_ } } function Get-ToolcacheNodeVersions { $toolcachePath = Join-Path $env:AGENT_TOOLSDIRECTORY "Node" - return Get-ChildItem $toolcachePath -Name | Sort-Object { [Version]$_ } + return Get-ChildItem $toolcachePath -Name | Sort-Object { [Version] $_ } } function Get-ToolcachePythonVersions { $toolcachePath = Join-Path $env:AGENT_TOOLSDIRECTORY "Python" - return Get-ChildItem $toolcachePath -Name | Sort-Object { [Version]$_ } + return Get-ChildItem $toolcachePath -Name | Sort-Object { [Version] $_ } } function Get-ToolcacheRubyVersions { $toolcachePath = Join-Path $env:AGENT_TOOLSDIRECTORY "Ruby" - return Get-ChildItem $toolcachePath -Name | Sort-Object { [Version]$_ } + return Get-ChildItem $toolcachePath -Name | Sort-Object { [Version] $_ } } function Get-ToolcachePyPyVersions { $toolcachePath = Join-Path $env:AGENT_TOOLSDIRECTORY "PyPy" Get-ChildItem -Path $toolcachePath -Name | Sort-Object { [Version] $_ } | ForEach-Object { $pypyRootPath = Join-Path $toolcachePath $_ "x86" - [string]$pypyVersionOutput = & "$pypyRootPath\python.exe" -c "import sys;print(sys.version)" + [string] $pypyVersionOutput = & "$pypyRootPath\python.exe" -c "import sys;print(sys.version)" $pypyVersionOutput -match "^([\d\.]+) \(.+\) \[PyPy ([\d\.]+\S*) .+]$" | Out-Null return "{0} [PyPy {1}]" -f $Matches[1], $Matches[2] } diff --git a/images/windows/scripts/docs-gen/SoftwareReport.Common.psm1 b/images/windows/scripts/docs-gen/SoftwareReport.Common.psm1 index 32e7b6b75..34a956b2a 100644 --- a/images/windows/scripts/docs-gen/SoftwareReport.Common.psm1 +++ b/images/windows/scripts/docs-gen/SoftwareReport.Common.psm1 @@ -5,7 +5,7 @@ function Initialize-RustEnvironment { } function Get-OSName { - return (Get-CimInstance -ClassName Win32_OperatingSystem).Caption | Take-Part -Part 1,2,3 + return (Get-CimInstance -ClassName Win32_OperatingSystem).Caption | Get-StringPart -Part 1,2,3 } function Get-OSVersion { @@ -26,75 +26,75 @@ function Get-BashVersion { } function Get-RustVersion { - rustc --version | Take-Part -Part 1 + rustc --version | Get-StringPart -Part 1 } function Get-RustupVersion { - cmd /c "rustup --version 2>NUL" | Take-Part -Part 1 + cmd /c "rustup --version 2>NUL" | Get-StringPart -Part 1 } function Get-RustCargoVersion { - cargo --version | Take-Part -Part 1 + cargo --version | Get-StringPart -Part 1 } function Get-RustdocVersion { - rustdoc --version | Take-Part -Part 1 + rustdoc --version | Get-StringPart -Part 1 } function Get-RustfmtVersion { - rustfmt --version | Take-Part -Part 1 | Take-Part -Part 0 -Delimiter ('-') + rustfmt --version | Get-StringPart -Part 1 | Get-StringPart -Part 0 -Delimiter ('-') } function Get-RustClippyVersion { - cargo clippy --version | Take-Part -Part 1 + cargo clippy --version | Get-StringPart -Part 1 } function Get-BindgenVersion { - bindgen --version | Take-Part -Part 1 + bindgen --version | Get-StringPart -Part 1 } function Get-CbindgenVersion { - cbindgen --version | Take-Part -Part 1 + cbindgen --version | Get-StringPart -Part 1 } function Get-CargoAuditVersion { - cargo-audit --version | Take-Part -Part 1 + cargo-audit --version | Get-StringPart -Part 1 } function Get-CargoOutdatedVersion { - cargo outdated --version | Take-Part -Part 1 + cargo outdated --version | Get-StringPart -Part 1 } function Get-PythonVersion { - python --version | Take-Part -Part 1 + python --version | Get-StringPart -Part 1 } function Get-PowershellCoreVersion { - pwsh --version | Take-Part -Part 1 + pwsh --version | Get-StringPart -Part 1 } function Get-RubyVersion { - ruby --version | Take-Part -Part 1 + ruby --version | Get-StringPart -Part 1 } function Get-GoVersion { - go version | Take-Part -Part 2 | Take-Part -Part 1 -Delimiter ('o') + go version | Get-StringPart -Part 2 | Get-StringPart -Part 1 -Delimiter ('o') } function Get-KotlinVersion { - cmd /c "kotlinc -version 2>&1" | Take-Part -Part 2 + cmd /c "kotlinc -version 2>&1" | Get-StringPart -Part 2 } function Get-PHPVersion { - php --version | Out-String | Take-Part -Part 1 + php --version | Out-String | Get-StringPart -Part 1 } function Get-JuliaVersion { - julia --version | Take-Part -Part 2 + julia --version | Get-StringPart -Part 2 } function Get-LLVMVersion { - (clang --version) -match "clang" | Take-Part -Part 2 + (clang --version) -match "clang" | Get-StringPart -Part 2 } function Get-PerlVersion { @@ -104,7 +104,7 @@ function Get-PerlVersion { } function Get-NodeVersion { - node --version | Take-Part -Part 0 -Delimiter ('v') + node --version | Get-StringPart -Part 0 -Delimiter ('v') } function Get-ChocoVersion { @@ -135,7 +135,7 @@ function Get-HelmVersion { } function Get-PipVersion { - (pip --version) -match "pip" | Take-Part -Part 1,4,5 + (pip --version) -match "pip" | Get-StringPart -Part 1, 4, 5 } function Get-CondaVersion { @@ -144,19 +144,19 @@ function Get-CondaVersion { } function Get-ComposerVersion { - composer --version | Take-Part -Part 2 + composer --version | Get-StringPart -Part 2 } function Get-NugetVersion { - (nuget help) -match "Nuget Version" | Take-Part -Part 2 + (nuget help) -match "Nuget Version" | Get-StringPart -Part 2 } function Get-AntVersion { - ant -version | Take-Part -Part 3 + ant -version | Get-StringPart -Part 3 } function Get-MavenVersion { - (mvn -version) -match "Apache Maven" | Take-Part -Part 2 + (mvn -version) -match "Apache Maven" | Get-StringPart -Part 2 } function Get-GradleVersion { @@ -166,16 +166,16 @@ function Get-GradleVersion { } function Get-SbtVersion { - (sbt -version) -match "sbt script" | Take-Part -Part 3 + (sbt -version) -match "sbt script" | Get-StringPart -Part 3 } function Get-DotnetSdks { $sdksRawList = dotnet --list-sdks - $sdkVersions = $sdksRawList | Foreach-Object {$_.Split()[0]} + $sdkVersions = $sdksRawList | Foreach-Object { $_.Split()[0] } $sdkPath = $sdksRawList[0].Split(' ', 2)[1] -replace '\[|]' [PSCustomObject]@{ Versions = $sdkVersions - Path = $sdkPath + Path = $sdkPath } } @@ -185,7 +185,7 @@ function Get-DotnetTools { $toolsList = @() - foreach ($dotnetTool in $dotnetTools) { + foreach ($dotnetTool in $dotnetTools) { $version = Invoke-Expression $dotnetTool.getversion $toolsList += [ToolVersionNode]::new($dotnetTool.name, $version) } @@ -194,21 +194,21 @@ function Get-DotnetTools { function Get-DotnetRuntimes { $runtimesRawList = dotnet --list-runtimes - $runtimesRawList | Group-Object {$_.Split()[0]} | ForEach-Object { + $runtimesRawList | Group-Object { $_.Split()[0] } | ForEach-Object { $runtimeName = $_.Name - $runtimeVersions = $_.Group | Foreach-Object {$_.split()[1]} + $runtimeVersions = $_.Group | Foreach-Object { $_.split()[1] } $runtimePath = $_.Group[0].Split(' ', 3)[2] -replace '\[|]' [PSCustomObject]@{ - "Runtime" = $runtimeName + "Runtime" = $runtimeName "Versions" = $runtimeVersions - "Path" = $runtimePath + "Path" = $runtimePath } } } function Get-DotnetFrameworkVersions { $path = "${env:ProgramFiles(x86)}\Microsoft SDKs\Windows\*\*\NETFX * Tools" - return Get-ChildItem -Path $path -Directory | ForEach-Object { $_.Name | Take-Part -Part 1 } + return Get-ChildItem -Path $path -Directory | ForEach-Object { $_.Name | Get-StringPart -Part 1 } } function Get-PowerShellAzureModules { @@ -276,20 +276,21 @@ function Get-CachedDockerImagesTableData { $parts = $_.Split("|") [PSCustomObject] @{ "Repository:Tag" = $parts[0] - "Digest" = $parts[1] - "Created" = $parts[2].split(' ')[0] + "Digest" = $parts[1] + "Created" = $parts[2].split(' ')[0] } } | Sort-Object -Property "Repository:Tag" } function Get-ShellTarget { - return Get-ChildItem C:\shells -File | Select-Object Name, @{n="Target";e={ - if ($_.Name -eq "msys2bash.cmd") { - "C:\msys64\usr\bin\bash.exe" - } else { - @($_.Target)[0] + return Get-ChildItem C:\shells -File | Select-Object Name, @{n = "Target"; e = { + if ($_.Name -eq "msys2bash.cmd") { + "C:\msys64\usr\bin\bash.exe" + } else { + @($_.Target)[0] + } } - }} | Sort-Object Name + } | Sort-Object Name } function Get-PacmanVersion { @@ -302,7 +303,7 @@ function Get-PacmanVersion { } function Get-YAMLLintVersion { - yamllint --version | Take-Part -Part 1 + yamllint --version | Get-StringPart -Part 1 } function Get-BizTalkVersion { diff --git a/images/windows/scripts/docs-gen/SoftwareReport.Helpers.psm1 b/images/windows/scripts/docs-gen/SoftwareReport.Helpers.psm1 index 7166911e8..c96afe997 100644 --- a/images/windows/scripts/docs-gen/SoftwareReport.Helpers.psm1 +++ b/images/windows/scripts/docs-gen/SoftwareReport.Helpers.psm1 @@ -17,7 +17,7 @@ function Get-PathWithLink { return "${inputPath}${link}" } -function Take-Part { +function Get-StringPart { param ( [Parameter(ValueFromPipeline)] [string] $toolOutput, @@ -27,4 +27,4 @@ function Take-Part { $parts = $toolOutput.Split($Delimiter, [System.StringSplitOptions]::RemoveEmptyEntries) $selectedParts = $parts[$Part] return [string]::Join($Delimiter, $selectedParts) -} \ No newline at end of file +} diff --git a/images/windows/scripts/docs-gen/SoftwareReport.Java.psm1 b/images/windows/scripts/docs-gen/SoftwareReport.Java.psm1 index d2d8232bb..cd6c077d3 100644 --- a/images/windows/scripts/docs-gen/SoftwareReport.Java.psm1 +++ b/images/windows/scripts/docs-gen/SoftwareReport.Java.psm1 @@ -2,7 +2,7 @@ function Get-JavaVersions { $defaultJavaPath = $env:JAVA_HOME $javaVersions = Get-Item env:JAVA_HOME_*_X64 $sortRules = @{ - Expression = { [Int32]$_.Name.Split("_")[2] } + Expression = { [Int32] $_.Name.Split("_")[2] } Descending = $false } @@ -19,4 +19,4 @@ function Get-JavaVersions { "Environment Variable" = $_.Name } } -} \ No newline at end of file +} diff --git a/images/windows/scripts/docs-gen/SoftwareReport.Tools.psm1 b/images/windows/scripts/docs-gen/SoftwareReport.Tools.psm1 index ce997c84f..2f4b76528 100644 --- a/images/windows/scripts/docs-gen/SoftwareReport.Tools.psm1 +++ b/images/windows/scripts/docs-gen/SoftwareReport.Tools.psm1 @@ -66,12 +66,12 @@ function Get-DockerComposeVersionV2 { } function Get-DockerWincredVersion { - $dockerCredVersion = docker-credential-wincred version | Take-Part -Part 2 | Take-Part -Part 0 -Delimiter "v" + $dockerCredVersion = docker-credential-wincred version | Get-StringPart -Part 2 | Get-StringPart -Part 0 -Delimiter "v" return $dockerCredVersion } function Get-GitVersion { - $gitVersion = git --version | Take-Part -Part -1 + $gitVersion = git --version | Get-StringPart -Part -1 return $gitVersion } diff --git a/images/windows/scripts/helpers/ChocoHelpers.ps1 b/images/windows/scripts/helpers/ChocoHelpers.ps1 index a4fe9e421..479facf1d 100644 --- a/images/windows/scripts/helpers/ChocoHelpers.ps1 +++ b/images/windows/scripts/helpers/ChocoHelpers.ps1 @@ -1,4 +1,25 @@ function Install-ChocoPackage { + <# + .SYNOPSIS + A function to install a Chocolatey package with retries. + + .DESCRIPTION + This function attempts to install a specified Chocolatey package. If the + installation fails, it retries a specified number of times. + + .PARAMETER PackageName + The name of the Chocolatey package to install. + + .PARAMETER ArgumentList + An array of arguments to pass to the choco install command. + + .PARAMETER RetryCount + The number of times to retry the installation if it fails. Default is 5. + + .EXAMPLE + Install-ChocoPackage -PackageName "git" -RetryCount 3 + #> + [CmdletBinding()] param( [Parameter(Mandatory)] @@ -30,6 +51,25 @@ function Install-ChocoPackage { } function Resolve-ChocoPackageVersion { + <# + .SYNOPSIS + Resolves the latest version of a Chocolatey package. + + .DESCRIPTION + This function takes a package name and a target version as input and returns the latest + version of the package that is greater than or equal to the target version. + + .PARAMETER PackageName + The name of the Chocolatey package. + + .PARAMETER TargetVersion + The target version of the package. + + .EXAMPLE + Resolve-ChocoPackageVersion -PackageName "example-package" -TargetVersion "1.0.0" + Returns the latest version of the "example-package" that is greater than or equal to "1.0.0". + #> + param( [Parameter(Mandatory)] [string] $PackageName, @@ -38,12 +78,12 @@ function Resolve-ChocoPackageVersion { ) $versionNumbers = $TargetVersion.Split(".") - [int]$versionNumbers[-1] += 1 + [int] $versionNumbers[-1] += 1 $incrementedVersion = $versionNumbers -join "." $filterQuery = "`$filter=(Id eq '$PackageName') and (IsPrerelease eq false) and (Version ge '$TargetVersion') and (Version lt '$incrementedVersion')" $latestVersion = (Invoke-RestMethod "https://community.chocolatey.org/api/v2/Packages()?$filterQuery").properties.Version | Where-Object { $_ -Like "$TargetVersion.*" -or $_ -eq $TargetVersion } | - Sort-Object { [version]$_ } | + Sort-Object { [version] $_ } | Select-Object -Last 1 return $latestVersion diff --git a/images/windows/scripts/helpers/ImageHelpers.psm1 b/images/windows/scripts/helpers/ImageHelpers.psm1 index 826d4d989..5af1843e8 100644 --- a/images/windows/scripts/helpers/ImageHelpers.psm1 +++ b/images/windows/scripts/helpers/ImageHelpers.psm1 @@ -2,43 +2,55 @@ param() . $PSScriptRoot\AndroidHelpers.ps1 -. $PSScriptRoot\ChocoHelpers.ps1 -. $PSScriptRoot\InstallHelpers.ps1 -. $PSScriptRoot\PathHelpers.ps1 -. $PSScriptRoot\VisualStudioHelpers.ps1 +Export-ModuleMember -Function @( + 'Get-AndroidPackages' + 'Get-AndroidPlatformPackages' + 'Get-AndroidBuildToolPackages' + 'Get-AndroidInstalledPackages' +) +. $PSScriptRoot\ChocoHelpers.ps1 +Export-ModuleMember -Function @( + 'Install-ChocoPackage' + 'Resolve-ChocoPackageVersion' +) + +. $PSScriptRoot\InstallHelpers.ps1 +Export-ModuleMember -Function @( + 'Install-Binary' + 'Invoke-DownloadWithRetry' + 'Get-ToolsetContent' + 'Get-TCToolPath' + 'Get-TCToolVersionPath' + 'Test-IsWin22' + 'Test-IsWin19' + 'Expand-7ZipArchive' + 'Get-WindowsUpdateStates' + 'Invoke-ScriptBlockWithRetry' + 'Get-GithubReleasesByVersion' + 'Resolve-GithubReleaseAssetUrl' + 'Get-ChecksumFromGithubRelease' + 'Get-ChecksumFromUrl' + 'Test-FileChecksum' + 'Test-FileSignature' + 'Update-Environment' +) + +. $PSScriptRoot\PathHelpers.ps1 Export-ModuleMember -Function @( 'Mount-RegistryHive' 'Dismount-RegistryHive' 'Add-MachinePathItem' 'Add-DefaultPathItem' - 'Install-Binary' +) + +. $PSScriptRoot\VisualStudioHelpers.ps1 +Export-ModuleMember -Function @( 'Install-VisualStudio' - 'Get-ToolsetContent' - 'Get-TCToolVersionPath' - 'Get-TCToolPath' - 'Invoke-DownloadWithRetry' + 'Get-VisualStudioInstance' + 'Get-VisualStudioComponents' 'Get-VsixInfoFromMarketplace' 'Install-VSIXFromFile' 'Install-VSIXFromUrl' 'Get-VSExtensionVersion' - 'Test-IsWin22' - 'Test-IsWin19' - 'Install-ChocoPackage' - 'Resolve-ChocoPackageVersion' - 'Resolve-GithubReleaseAssetUrl' - 'Expand-7ZipArchive' - 'Invoke-ScriptBlockWithRetry' - 'Get-VsCatalogJsonPath' - 'Get-AndroidPackages' - 'Get-AndroidPlatformPackages' - 'Get-AndroidBuildToolPackages' - 'Get-AndroidInstalledPackages' - 'Get-VisualStudioInstance' - 'Get-VisualStudioComponents' - 'Get-WindowsUpdateStates' - 'Get-GithubReleaseAssetHash' - 'Test-FileChecksum' - 'Test-FileSignature' - 'Update-Environment' ) diff --git a/images/windows/scripts/helpers/InstallHelpers.ps1 b/images/windows/scripts/helpers/InstallHelpers.ps1 index ee859b834..5bb8d87a2 100644 --- a/images/windows/scripts/helpers/InstallHelpers.ps1 +++ b/images/windows/scripts/helpers/InstallHelpers.ps1 @@ -131,6 +131,33 @@ function Install-Binary { } function Invoke-DownloadWithRetry { + <# + .SYNOPSIS + Downloads a file from a given URL with retry functionality. + + .DESCRIPTION + The Invoke-DownloadWithRetry function downloads a file from the specified URL + to the specified path. It includes retry functionality in case the download fails. + + .PARAMETER Url + The URL of the file to download. + + .PARAMETER Path + The path where the downloaded file will be saved. If not provided, a temporary path + will be used. + + .EXAMPLE + Invoke-DownloadWithRetry -Url "https://example.com/file.zip" -Path "C:\Downloads\file.zip" + Downloads the file from the specified URL and saves it to the specified path. + + .EXAMPLE + Invoke-DownloadWithRetry -Url "https://example.com/file.zip" + Downloads the file from the specified URL and saves it to a temporary path. + + .OUTPUTS + The path where the downloaded file is saved. + #> + Param ( [Parameter(Mandatory)] @@ -165,6 +192,11 @@ function Invoke-DownloadWithRetry { $attemptSeconds = [math]::Round(($(Get-Date) - $attemptStartTime).TotalSeconds, 2) Write-Warning "Package download failed in $attemptSeconds seconds" Write-Warning $_.Exception.Message + + if ($_.Exception.InnerException.Response.StatusCode -eq [System.Net.HttpStatusCode]::NotFound) { + Write-Warning "Request returned 404 Not Found. Aborting download." + $retries = 0 + } } if ($retries -eq 0) { @@ -180,6 +212,15 @@ function Invoke-DownloadWithRetry { } function Get-ToolsetContent { + <# + .SYNOPSIS + Retrieves the content of the toolset.json file. + + .DESCRIPTION + This function reads the toolset.json file located at "C:\image\toolset.json" + and returns the content as a PowerShell object. + #> + $toolsetPath = Join-Path "C:\\image" "toolset.json" $toolsetJson = Get-Content -Path $toolsetPath -Raw ConvertFrom-Json -InputObject $toolsetJson @@ -478,6 +519,10 @@ function Get-GithubReleasesByVersion { .PARAMETER AllowPrerelease Specifies whether to include prerelease versions in the results. By default, prerelease versions are excluded. + + .PARAMETER WithAssetsOnly + Specifies whether to exclude releases without assets. By default, releases without + assets are included. .EXAMPLE Get-GithubReleasesByVersion -Repository "Microsoft/PowerShell" -Version "7.2.0" @@ -500,7 +545,8 @@ function Get-GithubReleasesByVersion { [Alias("Repo")] [string] $Repository, [string] $Version = "*", - [switch] $AllowPrerelease + [switch] $AllowPrerelease, + [switch] $WithAssetsOnly ) $localCacheFile = Join-Path ${env:TEMP} "github-releases_$($Repository -replace "/", "_").json" @@ -528,7 +574,9 @@ function Get-GithubReleasesByVersion { throw "Failed to get releases from ${Repository}" } - $releases = $releases.Where{ $_.assets } + if ($WithAssetsOnly) { + $releases = $releases.Where{ $_.assets } + } if (-not $AllowPrerelease) { $releases = $releases.Where{ $_.prerelease -eq $false } } @@ -608,7 +656,8 @@ function Resolve-GithubReleaseAssetUrl { $matchingReleases = Get-GithubReleasesByVersion ` -Repository $Repository ` -AllowPrerelease:$AllowPrerelease ` - -Version $Version + -Version $Version ` + -WithAssetsOnly # Add wildcard to the beginning of the pattern if it's not there if ($UrlMatchPattern.Substring(0, 2) -ne "*/") { @@ -618,7 +667,7 @@ function Resolve-GithubReleaseAssetUrl { # Loop over releases until we find a download url matching the pattern foreach ($release in $matchingReleases) { $matchedVersion = $release.version - $matchedUrl = $release.assets.browser_download_url -like $UrlMatchPattern + $matchedUrl = ([string[]] $release.assets.browser_download_url) -like $UrlMatchPattern if ($matchedUrl) { break } @@ -638,13 +687,13 @@ function Resolve-GithubReleaseAssetUrl { return ($matchedUrl -as [string]) } -function Get-GithubReleaseAssetHash { +function Get-ChecksumFromGithubRelease { <# .SYNOPSIS Retrieves the hash value of a specific file from a GitHub release body. .DESCRIPTION - The Get-GithubReleaseAssetHash function retrieves the hash value (SHA256 or SHA512) + The Get-ChecksumFromGithubRelease function retrieves the hash value (SHA256 or SHA512) of a specific file from a GitHub release. It searches for the file in the release body and returns the hash value if found. @@ -666,12 +715,12 @@ function Get-GithubReleaseAssetHash { The type of hash value to retrieve. Valid values are "SHA256" and "SHA512". .EXAMPLE - Get-GithubReleaseAssetHash -Repository "MyRepo" -FileName "myfile.txt" -HashType "SHA256" + Get-ChecksumFromGithubRelease -Repository "MyRepo" -FileName "myfile.txt" -HashType "SHA256" Retrieves the SHA256 hash value of "myfile.txt" from the latest release of the "MyRepo" repository. .EXAMPLE - Get-GithubReleaseAssetHash -Repository "MyRepo" -Version "1.0" -FileName "myfile.txt" -HashType "SHA512" + Get-ChecksumFromGithubRelease -Repository "MyRepo" -Version "1.0" -FileName "myfile.txt" -HashType "SHA512" Retrieves the SHA512 hash value of "myfile.txt" from the release version "1.0" of the "MyRepo" repository. #> @@ -693,7 +742,8 @@ function Get-GithubReleaseAssetHash { $matchingReleases = Get-GithubReleasesByVersion ` -Repository $Repository ` -AllowPrerelease:$AllowPrerelease ` - -Version $Version + -Version $Version ` + -WithAssetsOnly foreach ($release in $matchingReleases) { $matchedVersion = $release.version @@ -728,6 +778,70 @@ function Get-GithubReleaseAssetHash { return $hash } +function Get-ChecksumFromUrl { + <# + .SYNOPSIS + Retrieves the checksum hash for a file from a given URL. + + .DESCRIPTION + The Get-ChecksumFromUrl function retrieves the checksum hash for a specified file + from a given URL. It supports SHA256 and SHA512 hash types. + + .PARAMETER Url + The URL of the checksum file. + + .PARAMETER FileName + The name of the file to retrieve the checksum hash for. + + .PARAMETER HashType + The type of hash to retrieve. Valid values are "SHA256" and "SHA512". + + .EXAMPLE + Get-ChecksumFromUrl -Url "https://example.com/checksums.txt" -FileName "file.txt" -HashType "SHA256" + Retrieves the SHA256 checksum hash for the file "file.txt" from the URL "https://example.com/checksums.txt". + #> + + param ( + [Parameter(Mandatory = $true)] + [string] $Url, + [Parameter(Mandatory = $true)] + [Alias("File", "Asset")] + [string] $FileName, + [Parameter(Mandatory = $true)] + [ValidateSet("SHA256", "SHA512")] + [Alias("Type")] + [string] $HashType + ) + + $tempFile = Join-Path -Path $env:TEMP -ChildPath ([System.IO.Path]::GetRandomFileName()) + $checksums = (Invoke-DownloadWithRetry -Url $Url -Path $tempFile | Get-Item | Get-Content) -as [string[]] + Remove-Item -Path $tempFile + + $matchedLine = $checksums | Where-Object { $_ -like "*$FileName*" } + if ($matchedLine.Count -gt 1) { + throw "Found multiple lines matching file name '${FileName}' in checksum file." + } elseif ($matchedLine.Count -eq 0) { + throw "File name '${FileName}' not found in checksum file." + } + + if ($HashType -eq "SHA256") { + $pattern = "[A-Fa-f0-9]{64}" + } elseif ($HashType -eq "SHA512") { + $pattern = "[A-Fa-f0-9]{128}" + } else { + throw "Unknown hash type: ${HashType}" + } + Write-Debug "Found line matching file name '${FileName}' in checksum file:`n${matchedLine}" + + $hash = $matchedLine | Select-String -Pattern $pattern | ForEach-Object { $_.Matches.Value } + if ([string]::IsNullOrEmpty($hash)) { + throw "Found '${FileName}' in checksum file, but failed to get hash from it.`nLine: ${matchedLine}" + } + Write-Host "Found hash for ${FileName} in checksum file: $hash" + + return $hash +} + function Test-FileChecksum { <# .SYNOPSIS diff --git a/images/windows/scripts/helpers/PathHelpers.ps1 b/images/windows/scripts/helpers/PathHelpers.ps1 index 192c8b978..1ddb9bc99 100644 --- a/images/windows/scripts/helpers/PathHelpers.ps1 +++ b/images/windows/scripts/helpers/PathHelpers.ps1 @@ -18,9 +18,9 @@ function Mount-RegistryHive { #> param( [Parameter(Mandatory = $true)] - [string]$FileName, + [string] $FileName, [Parameter(Mandatory = $true)] - [string]$SubKey + [string] $SubKey ) Write-Host "Loading the file $FileName to the Key $SubKey" @@ -31,8 +31,7 @@ function Mount-RegistryHive { $result = reg load $SubKey $FileName *>&1 if ($LASTEXITCODE -ne 0) { - Write-Error "Failed to load file $FileName to the key ${SubKey}: $result" - exit 1 + throw "Failed to load file $FileName to the key ${SubKey}: $result" } } @@ -52,7 +51,7 @@ function Dismount-RegistryHive { #> param( [Parameter(Mandatory = $true)] - [string]$SubKey + [string] $SubKey ) Write-Host "Unloading the hive $SubKey" @@ -87,12 +86,12 @@ function Add-MachinePathItem { param( [Parameter(Mandatory = $true)] - [string]$PathItem + [string] $PathItem ) $currentPath = [System.Environment]::GetEnvironmentVariable("PATH", "Machine") $newPath = $PathItem + ';' + $currentPath - [System.Environment]::SetEnvironmentVariable("PATH", $newPath, "Machine") + [Environment]::SetEnvironmentVariable("PATH", $newPath, "Machine") } function Add-DefaultPathItem { @@ -120,7 +119,7 @@ function Add-DefaultPathItem { param( [Parameter(Mandatory = $true)] - [string]$PathItem + [string] $PathItem ) Mount-RegistryHive ` diff --git a/images/windows/scripts/helpers/VisualStudioHelpers.ps1 b/images/windows/scripts/helpers/VisualStudioHelpers.ps1 index 99ef27b4d..79f60041c 100644 --- a/images/windows/scripts/helpers/VisualStudioHelpers.ps1 +++ b/images/windows/scripts/helpers/VisualStudioHelpers.ps1 @@ -117,18 +117,65 @@ Function Install-VisualStudio { } function Get-VisualStudioInstance { + <# + .SYNOPSIS + Retrieves the Visual Studio instance. + + .DESCRIPTION + This function retrieves the Visual Studio instance + using the Get-VSSetupInstance cmdlet. + It searches for both regular and preview versions + of Visual Studio and returns the first instance found. + #> + # Use -Prerelease and -All flags to make sure that Preview versions of VS are found correctly $vsInstance = Get-VSSetupInstance -Prerelease -All | Where-Object { $_.DisplayName -match "Visual Studio" } | Select-Object -First 1 $vsInstance | Select-VSSetupInstance -Product * } function Get-VisualStudioComponents { - (Get-VisualStudioInstance).Packages | Where-Object type -in 'Component', 'Workload' | - Sort-Object Id, Version | Select-Object @{n = 'Package'; e = { $_.Id } }, Version | - Where-Object { $_.Package -notmatch "[0-9a-fA-F]{8}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{12}" } + <# + .SYNOPSIS + Retrieves the Visual Studio components. + + .DESCRIPTION + This function retrieves the Visual Studio components + by filtering the packages returned by Get-VisualStudioInstance cmdlet. + It filters the packages based on their type, sorts them by Id and Version, + and excludes packages with GUID-like Ids. + #> + + (Get-VisualStudioInstance).Packages ` + | Where-Object type -in 'Component', 'Workload' ` + | Sort-Object Id, Version ` + | Select-Object @{n = 'Package'; e = { $_.Id } }, Version ` + | Where-Object { $_.Package -notmatch "[0-9a-fA-F]{8}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{12}" } } function Get-VsixInfoFromMarketplace { + <# + .SYNOPSIS + Retrieves information about a Visual Studio extension from the Visual Studio Marketplace. + + .DESCRIPTION + The Get-VsixInfoFromMarketplace function retrieves information + about a Visual Studio extension from the Visual Studio Marketplace. + It takes the name of the extension as input and returns + the extension's name, VsixId, filename, and download URI. + + .PARAMETER Name + The name of the Visual Studio extension. + + .PARAMETER MarketplaceUri + The URI of the Visual Studio Marketplace. + Default value is "https://marketplace.visualstudio.com/items?itemName=". + + .EXAMPLE + Get-VsixInfoFromMarketplace -Name "ProBITools.MicrosoftReportProjectsforVisualStudio2022" + Retrieves information about the "ProBITools.MicrosoftReportProjectsforVisualStudio2022" extension + from the Visual Studio Marketplace. + #> + Param ( [Parameter(Mandatory)] @@ -183,6 +230,25 @@ function Get-VsixInfoFromMarketplace { } function Install-VSIXFromFile { + <# + .SYNOPSIS + Installs a Visual Studio Extension (VSIX) from a file. + + .DESCRIPTION + This function installs a Visual Studio Extension (VSIX) + from the specified file path. It uses the VSIXInstaller.exe + tool provided by Microsoft Visual Studio. + + .PARAMETER FilePath + The path to the VSIX file that needs to be installed. + + .PARAMETER Retries + The number of retries to attempt if the installation fails. Default is 20. + + .EXAMPLE + Install-VSIXFromFile -FilePath "C:\Extensions\MyExtension.vsix" -Retries 10 + Installs the VSIX file located at "C:\Extensions\MyExtension.vsix" with 10 retries in case of failure. + #> Param ( [Parameter(Mandatory = $true)] @@ -228,6 +294,25 @@ function Install-VSIXFromFile { } function Install-VSIXFromUrl { + <# + .SYNOPSIS + Installs a Visual Studio extension (VSIX) from a given URL. + + .DESCRIPTION + This function downloads a Visual Studio extension (VSIX) + from the specified URL and installs it. + + .PARAMETER Url + The URL of the VSIX file to download and install. + + .PARAMETER Retries + The number of retries to attempt if the download fails. Default is 20. + + .EXAMPLE + Install-VSIXFromUrl -Url "https://example.com/extension.vsix" -Retries 10 + Downloads and installs the VSIX file from the specified URL with 10 retries. + #> + Param ( [Parameter(Mandatory = $true)] @@ -241,6 +326,22 @@ function Install-VSIXFromUrl { } function Get-VSExtensionVersion { + <# + .SYNOPSIS + Retrieves the version of a Visual Studio extension package. + + .DESCRIPTION + This function retrieves the version of a specified Visual Studio extension package. + It searches for the package in the installed instances of Visual Studio and + returns the version number. + + .PARAMETER packageName + The name of the extension package. + + .EXAMPLE + Get-VSExtensionVersion -packageName "MyExtensionPackage" + Retrieves the version of the extension package named "MyExtensionPackage" for Visual Studio. + #> Param ( [Parameter(Mandatory = $true)] diff --git a/images/windows/scripts/tests/Haskell.Tests.ps1 b/images/windows/scripts/tests/Haskell.Tests.ps1 index 8381ff603..513b71ee5 100644 --- a/images/windows/scripts/tests/Haskell.Tests.ps1 +++ b/images/windows/scripts/tests/Haskell.Tests.ps1 @@ -1,16 +1,16 @@ Describe "Haskell" { $ghcPackagesPath = "c:\ghcup\ghc" - [array]$ghcVersionList = Get-ChildItem -Path $ghcPackagesPath -Filter "*" | ForEach-Object { $_.Name.Trim() } + [array] $ghcVersionList = Get-ChildItem -Path $ghcPackagesPath -Filter "*" | ForEach-Object { $_.Name.Trim() } $ghcCount = $ghcVersionList.Count - $defaultGhcVersion = $ghcVersionList | Sort-Object {[Version]$_} | Select-Object -Last 1 + $defaultGhcVersion = $ghcVersionList | Sort-Object {[Version] $_} | Select-Object -Last 1 $ghcDefaultCases = @{ defaultGhcVersion = $defaultGhcVersion - defaultGhcShortVersion = ([version]$defaultGhcVersion).ToString(3) + defaultGhcShortVersion = ([version] $defaultGhcVersion).ToString(3) } $ghcTestCases = $ghcVersionList | ForEach-Object { $ghcVersion = $_ - $ghcShortVersion = ([version]$ghcVersion).ToString(3) + $ghcShortVersion = ([version] $ghcVersion).ToString(3) $binGhcPath = Join-Path $ghcPackagesPath "$ghcShortVersion\bin\ghc.exe" @{ ghcVersion = $ghcVersion diff --git a/images/windows/scripts/tests/Java.Tests.ps1 b/images/windows/scripts/tests/Java.Tests.ps1 index 8245cf837..bf40a8d43 100644 --- a/images/windows/scripts/tests/Java.Tests.ps1 +++ b/images/windows/scripts/tests/Java.Tests.ps1 @@ -3,7 +3,7 @@ Describe "Java" { $defaultVersion = $toolsetJava.default $jdkVersions = $toolsetJava.versions - [array]$testCases = $jdkVersions | ForEach-Object { @{Version = $_ } } + [array] $testCases = $jdkVersions | ForEach-Object { @{Version = $_ } } It "Java is default" -TestCases @(@{ DefaultJavaVersion = $defaultVersion }) { $actualJavaPath = Get-EnvironmentVariable "JAVA_HOME"