From 13df66d137ca0e345b1bd2a311f4928e281dd09c Mon Sep 17 00:00:00 2001 From: sysadmin Date: Wed, 10 Jun 2026 17:21:27 +0100 Subject: [PATCH] feat(apps): bootstrap-winget downloads + installs the App Installer online (LTSC lacks it) --- windows/apps/bootstrap-winget.ps1 | 69 +++++++++++++++++++++++++++++-- 1 file changed, 66 insertions(+), 3 deletions(-) diff --git a/windows/apps/bootstrap-winget.ps1 b/windows/apps/bootstrap-winget.ps1 index 2e64179..6eaf9bc 100644 --- a/windows/apps/bootstrap-winget.ps1 +++ b/windows/apps/bootstrap-winget.ps1 @@ -1,5 +1,68 @@ #Requires -Version 5.1 -$ErrorActionPreference='SilentlyContinue' -# Register the inbox App Installer if present, else nothing to do (offline image w/o it). +# Provision winget (the App Installer) when absent. Windows IoT Enterprise LTSC +# ships WITHOUT the inbox Microsoft.DesktopAppInstaller package, so re-registering +# it is not enough - we download and install it (plus dependencies) online at apply +# time. Best-effort and idempotent: exit 0 if winget ends up available, else 1. +$ErrorActionPreference = 'SilentlyContinue' + +function Test-Winget { + return [bool](Get-Command winget -ErrorAction SilentlyContinue) +} + +# Fast path 1: winget already on PATH. +if (Test-Winget) { exit 0 } + +# Fast path 2: an inbox App Installer package is present - just re-register it. Get-AppxPackage -AllUsers Microsoft.DesktopAppInstaller | - ForEach-Object { Add-AppxPackage -DisableDevelopmentMode -Register "$($_.InstallLocation)\AppXManifest.xml" } + ForEach-Object { Add-AppxPackage -DisableDevelopmentMode -Register "$($_.InstallLocation)\AppXManifest.xml" } +if (Test-Winget) { exit 0 } + +# Slow path: download + install the App Installer and its dependencies online. +$temp = $env:TEMP +$bundlePath = Join-Path $temp 'Microsoft.DesktopAppInstaller.msixbundle' +$vclibsPath = Join-Path $temp 'Microsoft.VCLibs.x64.14.00.Desktop.appx' +$uixamlNupkg = Join-Path $temp 'microsoft.ui.xaml.2.8.6.nupkg' +$uixamlExtract = Join-Path $temp 'uixaml.2.8.6' +$uixamlAppx = Join-Path $uixamlExtract 'tools\AppX\x64\Release\Microsoft.UI.Xaml.2.8.appx' + +$bundleUrl = 'https://aka.ms/getwinget' +$vclibsUrl = 'https://aka.ms/Microsoft.VCLibs.x64.14.00.Desktop.appx' +$uixamlUrl = 'https://globalcdn.nuget.org/packages/microsoft.ui.xaml.2.8.6.nupkg' + +function Get-File { + param([string]$Url, [string]$Destination) + try { + Invoke-WebRequest -Uri $Url -OutFile $Destination -UseBasicParsing + return (Test-Path $Destination) + } catch { + return $false + } +} + +# Download the App Installer bundle (required). +if (-not (Get-File -Url $bundleUrl -Destination $bundlePath)) { exit 1 } + +# Download the VCLibs desktop dependency (required). +if (-not (Get-File -Url $vclibsUrl -Destination $vclibsPath)) { exit 1 } + +# Download the UI.Xaml 2.8 nuget package (a .zip) and extract the appx from it. +if (-not (Get-File -Url $uixamlUrl -Destination $uixamlNupkg)) { exit 1 } +try { + if (Test-Path $uixamlExtract) { Remove-Item -Path $uixamlExtract -Recurse -Force } + Add-Type -AssemblyName System.IO.Compression.FileSystem + [System.IO.Compression.ZipFile]::ExtractToDirectory($uixamlNupkg, $uixamlExtract) +} catch { + exit 1 +} +if (-not (Test-Path $uixamlAppx)) { exit 1 } + +# Install order: VCLibs dependency, then UI.Xaml dependency, then the bundle with +# both supplied as DependencyPath. Per-user Add-AppxPackage (toolbox runs as the +# real admin user at first logon). +Add-AppxPackage -Path $vclibsPath +Add-AppxPackage -Path $uixamlAppx +Add-AppxPackage -Path $bundlePath -DependencyPath $vclibsPath, $uixamlAppx + +# Final re-check. +if (Test-Winget) { exit 0 } +exit 1