Skip to content

Commit

Permalink
Refine Windows 10 SDK install command and add tests (#153)
Browse files Browse the repository at this point in the history
  • Loading branch information
mokagio authored Feb 26, 2025
1 parent 18cf230 commit 356fe9b
Show file tree
Hide file tree
Showing 6 changed files with 275 additions and 23 deletions.
95 changes: 95 additions & 0 deletions .buildkite/pipeline.yml
Original file line number Diff line number Diff line change
Expand Up @@ -118,3 +118,98 @@ steps:
notify:
- github_commit_status:
context: "pr_changed_files Tests: Edge Cases"

- group: ":windows: install_windows_10_sdk Tests"
steps:
- label: ":windows: install_windows_10_sdk Tests - Version file with valid format and version"
command: |
"20348" | Out-File .windows-10-sdk-version
.\tests\test-install-windows-10-sdk.ps1 -ExpectedExitCode 0
agents:
queue: windows
notify:
- github_commit_status:
context: "install_windows_10_sdk Tests - Version file with valid format and version"

- label: ":windows: install_windows_10_sdk Tests - Version file with one new line"
command: |
"20348`n" | Out-File .windows-10-sdk-version
.\tests\test-install-windows-10-sdk.ps1 -ExpectedExitCode 0
agents:
queue: windows
notify:
- github_commit_status:
context: "install_windows_10_sdk Tests - Version file with one new line"

- label: ":windows: install_windows_10_sdk Tests - Version file with more than one new line"
command: |
"20348`n`n" | Out-File .windows-10-sdk-version
.\tests\test-install-windows-10-sdk.ps1 -ExpectedExitCode 0
agents:
queue: windows
notify:
- github_commit_status:
context: "install_windows_10_sdk Tests - Version file with more than one new line"

- label: ":windows: install_windows_10_sdk Tests - Version file with leading whitespaces"
command: |
" 19041" | Out-File .windows-10-sdk-version
.\tests\test-install-windows-10-sdk.ps1 -ExpectedExitCode 0
agents:
queue: windows
notify:
- github_commit_status:
context: "install_windows_10_sdk Tests - Version file with leading whitespaces"

- label: ":windows: install_windows_10_sdk Tests - Version file with trailing whitespaces"
command: |
"18362 " | Out-File .windows-10-sdk-version
.\tests\test-install-windows-10-sdk.ps1 -ExpectedExitCode 0
agents:
queue: windows
notify:
- github_commit_status:
context: "install_windows_10_sdk Tests - Version file with trailing whitespaces"

- label: ":windows: install_windows_10_sdk Tests - Version file with a word"
command: |
"not an integer" | Out-File .windows-10-sdk-version
.\tests\test-install-windows-10-sdk.ps1 `
-ExpectedExitCode 1 `
-ExpectedErrorKeyphrase "Expected an integer"
agents:
queue: windows
notify:
- github_commit_status:
context: "install_windows_10_sdk Tests - Version file with a word"

- label: ":windows: install_windows_10_sdk Tests - Missing version file"
command: |
.\tests\test-install-windows-10-sdk.ps1 `
-ExpectedExitCode 1 `
-ExpectedErrorKeyphrase "No Windows 10 SDK version file found at .windows-10-sdk-version"
agents:
queue: windows
notify:
- github_commit_status:
context: "install_windows_10_sdk Tests - Version file with a word"

- label: ":windows: install_windows_10_sdk Tests - Version file with version number that is not in the allowed list"
command: |
"12345" | Out-File .windows-10-sdk-version
.\tests\test-install-windows-10-sdk.ps1 `
-ExpectedExitCode 1 `
-ExpectedErrorKeyphrase "Invalid Windows 10 SDK version: 12345"
agents:
queue: windows
notify:
- github_commit_status:
context: "install_windows_10_sdk Tests - Version file with version number that is not in the allowed list"

- label: ":windows: prepare_windows_host_for_app_distribution Tests - Skip Windows 10 SDK Installation"
command: .\tests\test-prepare-windows-host-for-app-distribution.ps1
agents:
queue: windows
notify:
- github_commit_status:
context: "prepare_windows_host_for_app_distribution Tests - Skip Windows 10 SDK Installation"
55 changes: 51 additions & 4 deletions bin/install_windows_10_sdk.ps1
Original file line number Diff line number Diff line change
@@ -1,17 +1,64 @@
# Install the Windows 10 SDK and Visual Studio Build Tools using the value in .windows-10-sdk-version.
#
# The expected .windows-10-sdk-version format is a integer representing a valid SDK component id.
# The list of valid component ids can be found at
# https://learn.microsoft.com/en-us/visualstudio/install/workload-component-id-vs-build-tools?view=vs-2022
#
# Example:
#
# 20348

param (
[switch]$DryRun = $false
)

# Stop script execution when a non-terminating error occurs
$ErrorActionPreference = "Stop"

Write-Host "--- :windows: Installing Windows 10 SDK and Visual Studio Build Tools"
Write-Output "--- :windows: Installing Windows 10 SDK and Visual Studio Build Tools"

# See list at https://learn.microsoft.com/en-us/visualstudio/install/workload-component-id-vs-build-tools?view=vs-2022
$allowedVersions = @(
"20348",
"19041",
"18362",
"17763",
"17134",
"16299",
"15063",
"14393"
)

$windowsSDKVersionFile = ".windows-10-sdk-version"
if (-not (Test-Path $windowsSDKVersionFile)) {
Write-Output "[!] No Windows 10 SDK version file found at $windowsSDKVersionFile."
exit 1
}

$windows10SDKVersion = Get-Content $windowsSDKVersionFile
$windows10SDKVersion = (Get-Content -TotalCount 1 $windowsSDKVersionFile).Trim()

if ($windows10SDKVersion -notmatch '^\d+$') {
Write-Output "[!] Invalid version file format."
Write-Output "Expected an integer, got: '$windows10SDKVersion'"
exit 1
}

if ($allowedVersions -notcontains $windows10SDKVersion) {
Write-Output "[!] Invalid Windows 10 SDK version: $windows10SDKVersion"
Write-Output "Allowed versions are:"
foreach ($version in $allowedVersions) {
Write-Output "- $version"
}
Write-Output "More info at https://learn.microsoft.com/en-us/visualstudio/install/workload-component-id-vs-build-tools?view=vs-2022"
exit 1
}

Write-Output "Will attempt to set up Windows 10 ($windows10SDKVersion) SDK and Visual Studio Build Tools..."

Write-Host "Will attempt to set up Windows 10 ($windows10SDKVersion) SDK and Visual Studio Build Tools..."
if ($DryRun) {
Write-Output "Running in dry run mode, finishing here."
exit 0
}

# Download the Visual Studio Build Tools Bootstrapper
Write-Output "~~~ Downloading Visual Studio Build Tools..."
Expand All @@ -26,7 +73,7 @@ If (-not (Test-Path $buildToolsPath)) {
Write-Output "[!] Failed to download Visual Studio Build Tools"
Exit 1
} else {
Write-Output "Successfully downloaded Visual Studio Build Toosl at $buildToolsPath."
Write-Output "Successfully downloaded Visual Studio Build Tools at $buildToolsPath."
}

# Install the Windows SDK and other required components
Expand Down
6 changes: 3 additions & 3 deletions bin/path_aware_refreshenv.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,10 @@
# Stop script execution when a non-terminating error occurs
$ErrorActionPreference = "Stop"

Write-Host "PATH before refreshenv is $env:PATH"
Write-Output "PATH before refreshenv is $env:PATH"
$originalPath = "$env:PATH"
Write-Host "Calling refreshenv..."
Write-Output "Calling refreshenv..."
refreshenv
$mergedPath = "$env:PATH;$originalPath" -split ";" | Select-Object -Unique -Skip 1
$env:PATH = ($mergedPath -join ";")
Write-Host "PATH after refreshenv is $env:PATH"
Write-Output "PATH after refreshenv is $env:PATH"
41 changes: 25 additions & 16 deletions bin/prepare_windows_host_for_app_distribution.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -8,24 +8,28 @@
# - Install the Windows 10 SDK if it detected a `.windows-10-sdk-version` file(2)
#
# (1) The certificate it installs is stored in our AWS SecretsManager storage (`windows-code-signing-certificate` secret ID)
# (2) You can skip the Win10 install even if `.windows-10-sdk-version` file is present by using the `SKIP_WINDOWS_10_SDK_INSTALL=1` env var before calling this script
# (2) You can skip the Windows 10 SDK installation regardless of whether `.windows-10-sdk-version` is present by calling the script with `-SkipWindows10SDKInstallation`.
#
# Note: In addition to calling this script, and depending on your client app, you might want to also install `npm` and the `Node.js` packages used by your client app on the agent too. For that part, you should use the `automattic/nvm` Buildkite plugin on the pipeline step's `plugins:` attribute.
#

param (
[switch]$SkipWindows10SDKInstallation = $false
)

# Stop script execution when a non-terminating error occurs
$ErrorActionPreference = "Stop"

Write-Host "--- :windows: Setting up Windows for app distribution"
Write-Output "--- :windows: Setting up Windows for app distribution"

Write-Host "Current working directory: $PWD"
Write-Output "Current working directory: $PWD"

Write-Host "Enable long path behavior"
Write-Output "Enable long path behavior"
# See https://docs.microsoft.com/en-us/windows/desktop/fileio/naming-a-file#maximum-path-length-limitation
Set-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Control\FileSystem' -Name 'LongPathsEnabled' -Value 1

# Disable Windows Defender before starting – otherwise our performance is terrible
Write-Host "Disable Windows Defender..."
Write-Output "Disable Windows Defender..."
$avPreference = @(
@{DisableArchiveScanning = $true}
@{DisableAutoExclusions = $true}
Expand Down Expand Up @@ -61,54 +65,59 @@ $avPreference | Foreach-Object {
# https://docs.microsoft.com/en-us/microsoft-365/security/defender-endpoint/microsoft-defender-antivirus-compatibility?view=o365-worldwide
$atpRegPath = 'HKLM:\SOFTWARE\Policies\Microsoft\Windows Advanced Threat Protection'
if (Test-Path $atpRegPath) {
Write-Host "Set Microsoft Defender Antivirus to passive mode"
Write-Output "Set Microsoft Defender Antivirus to passive mode"
Set-ItemProperty -Path $atpRegPath -Name 'ForceDefenderPassiveMode' -Value '1' -Type 'DWORD'
}

# From https://stackoverflow.com/a/46760714
Write-Host "--- :windows: Setting up Package Manager"
Write-Output "--- :windows: Setting up Package Manager"
$env:ChocolateyInstall = Convert-Path "$((Get-Command choco).Path)\..\.."
Import-Module "$env:ChocolateyInstall\helpers\chocolateyProfile.psm1"

# This should avoid issues with symlinks not being supported in Windows.
#
# See how this build failed
# https://buildkite.com/automattic/beeper-desktop/builds/2895#01919738-7c6e-4b82-8d1d-1c1800481740
Write-Host "--- :windows: :linux: Enable developer mode to use symlinks"
Write-Output "--- :windows: :linux: Enable developer mode to use symlinks"

$developerMode = Get-WindowsOptionalFeature -Online -FeatureName Microsoft-Windows-Subsystem-Linux

if ($developerMode.State -eq 'Enabled') {
Write-Host "Developer Mode is already enabled."
Write-Output "Developer Mode is already enabled."
} else {
Write-Host "Enabling Developer Mode..."
Write-Output "Enabling Developer Mode..."
try {
Enable-WindowsOptionalFeature -Online -FeatureName Microsoft-Windows-Subsystem-Linux -NoRestart
} catch {
Write-Host "Failed to enable Developer Mode. Continuing without it..."
Write-Output "Failed to enable Developer Mode. Continuing without it..."
}
}

Write-Host "--- :lock_with_ink_pen: Download Code Signing Certificate"
Write-Output "--- :lock_with_ink_pen: Download Code Signing Certificate"
$certificateBinPath = "certificate.bin"
$EncodedText = aws secretsmanager get-secret-value --secret-id windows-code-signing-certificate `
| jq -r '.SecretString' `
| Out-File $certificateBinPath
$certificatePfxPath = "certificate.pfx"
certutil -decode $certificateBinPath $certificatePfxPath
Write-Host "Code signing certificate downloaded at: $((Get-Item $certificatePfxPath).FullName)"
Write-Output "Code signing certificate downloaded at: $((Get-Item $certificatePfxPath).FullName)"

Write-Host "--- :windows: Checking whether to install Windows 10 SDK..."
Write-Output "--- :windows: Checking whether to install Windows 10 SDK..."

# When using Electron Forge and electron2appx, building Appx requires the Windows 10 SDK
#
# See https://github.com/hermit99/electron-windows-store/tree/v2.1.2?tab=readme-ov-file#usage

if ($SkipWindows10SDKInstallation) {
Write-Output "Run with SkipWindows10SDKInstallation = true. Skipping Windows 10 SDK installation check."
exit 0
}

$windowsSDKVersionFile = ".windows-10-sdk-version"
if (Test-Path $windowsSDKVersionFile) {
Write-Host "Found $windowsSDKVersionFile file, installing Windows 10 SDK..."
Write-Output "Found $windowsSDKVersionFile file, installing Windows 10 SDK..."
& "$PSScriptRoot\install_windows_10_sdk.ps1"
If ($LastExitCode -ne 0) { Exit $LastExitCode }
} else {
Write-Host "No $windowsSDKVersionFile file found, skipping Windows 10 SDK installation."
Write-Output "No $windowsSDKVersionFile file found, skipping Windows 10 SDK installation."
}
48 changes: 48 additions & 0 deletions tests/test-install-windows-10-sdk.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
param (
[int]$ExpectedExitCode = 0,
[string]$ExpectedErrorKeyphrase = ""
)

# Ensure the output is UTF-8 encoded so we can use emojis...
[System.Console]::OutputEncoding = [System.Text.Encoding]::UTF8
$emojiGreenCheck = "$([char]0x2705)"
$emojiRedCross = "$([char]0x274C)"

Write-Output "Running test-install-windows-10-sdk.ps1 with ExpectedExitCode=$ExpectedExitCode and ExpectedErrorKeyphrase=$ExpectedErrorKeyphrase"

if (($ExpectedExitCode -eq 0) -and ($ExpectedErrorKeyphrase -ne "")) {
Write-Output "$emojiRedCross Expected call to succeed (expected error code = 0), but given an error keyphrase to check."
exit 1
}

$output = & "$PSScriptRoot\..\bin\install_windows_10_sdk.ps1" -DryRun
$exitCode = $LASTEXITCODE

if ($exitCode -ne $ExpectedExitCode) {
Write-Output "$emojiRedCross Expected exit code $ExpectedExitCode, got $exitCode"
Write-Output "Output was:"
Write-Output "$output"
exit 1
} else {
Write-Output "$emojiGreenCheck Exit code matches expected value ($ExpectedExitCode)"
}

# Only check error keyphrase if exit code is not 0
if ($exitCode -eq 0) {
exit 0
}

# If keyphrase is empty, assume the caller is satisfied with only testing the exit code
if ($ExpectedErrorKeyphrase -eq "") {
Write-Output "Exit code match expectation and no error keyphrase was provided. Test completed."
exit 0
}

if ($output -match [regex]::Escape($ExpectedErrorKeyphrase)) {
Write-Output "$emojiGreenCheck Error keyphrase matches expected value ($ExpectedErrorKeyphrase)"
Write-Output "Test completed."
} else {
Write-Output "$emojiRedCross Expected error to contain '$ExpectedErrorKeyphrase', but got:"
Write-Output "$output"
exit 1
}
53 changes: 53 additions & 0 deletions tests/test-prepare-windows-host-for-app-distribution.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# Tests the prepare_windows_host_for_app_distribution.ps1 script with the -SkipWindows10SDKInstallation parameter.
#
# We only test the skip behavior because the installation takes a "long" time to run.

param (
[int]$ExpectedExitCode = 0
)

# Ensure the output is UTF-8 encoded so we can use emojis...
[System.Console]::OutputEncoding = [System.Text.Encoding]::UTF8
$emojiGreenCheck = "$([char]0x2705)"
$emojiRedCross = "$([char]0x274C)"

Write-Output "Testing prepare_windows_host_for_app_distribution.ps1 with -SkipWindows10SDKInstallation"

# Create a valid SDK version file to ensure it's not being used
$sdkVersion = "20348"
"$sdkVersion" | Out-File .windows-10-sdk-version

# Run the script with skip parameter
$output = & "$PSScriptRoot\..\bin\prepare_windows_host_for_app_distribution.ps1" -SkipWindows10SDKInstallation
$exitCode = $LASTEXITCODE

# Check exit code
if ($exitCode -ne $ExpectedExitCode) {
Write-Output "$emojiRedCross Expected exit code $ExpectedExitCode, got $exitCode"
Write-Output "Output was:"
Write-Output "$output"
exit 1
} else {
Write-Output "$emojiGreenCheck Exit code matches expected value ($ExpectedExitCode)"
}

$expectedSkipMessage = "Run with SkipWindows10SDKInstallation = true. Skipping Windows 10 SDK installation check."
if ($output -match [regex]::Escape($expectedSkipMessage)) {
Write-Output "$emojiGreenCheck Found expected skip message in output"
} else {
Write-Output "$emojiRedCross Expected to find message about skipping due to parameter, but got:"
Write-Output "$output"
exit 1
}

# Verify SDK was not installed by checking the file system
$windowsSDKsRoot = "C:\Program Files (x86)\Windows Kits\10\bin"
$sdkPath = "$windowsSDKsRoot\10.0.$sdkVersion\x64"
If (Test-Path $sdkPath) {
Write-Output "$emojiRedCross Found SDK installation at $sdkPath when it should have been skipped"
exit 1
} else {
Write-Output "$emojiGreenCheck Confirmed SDK was not installed at $sdkPath"
}

Write-Output "Test completed successfully."

0 comments on commit 356fe9b

Please sign in to comment.