Public/Update-MSIXTooling.ps1

function Update-MSIXTooling {
<#
.SYNOPSIS
    Downloads and installs the latest MSIX packaging tools.

.DESCRIPTION
    Downloads two sets of tools into the module's Libs folder:

      1. MSIX Core (msixmgr.exe)
         Source: https://github.com/microsoft/msix-packaging
         Target: Libs\MSIXCore\

      2. Windows SDK Packaging Tools (makeappx.exe, signtool.exe)
         Source: https://github.com/microsoft/MSIX-Toolkit
         Target: Libs\MSIXPackaging\

    Each tool is only downloaded when missing or a newer release is available.
    Use -SkipCore or -SkipPackaging to download only one of the two.

.PARAMETER Force
    Suppresses all confirmation prompts.

.PARAMETER SkipCore
    Skip downloading MSIX Core (msixmgr.exe).

.PARAMETER SkipPackaging
    Skip downloading the Windows SDK Packaging Tools (makeappx, signtool).

.EXAMPLE
    Update-MSIXTooling

.EXAMPLE
    Update-MSIXTooling -Force

.EXAMPLE
    Update-MSIXTooling -SkipCore

.NOTES
    MSIX Core source : https://github.com/microsoft/msix-packaging
    MSIX Toolkit source : https://github.com/microsoft/MSIX-Toolkit
    Andreas Nick, 2026
#>


    [CmdletBinding(SupportsShouldProcess)]
    param(
        [Switch] $Force,
        [Switch] $SkipCore,
        [Switch] $SkipPackaging
    )

    # --- MSIX Core (msixmgr.exe) ---
    if (-not $SkipCore) {
        $msixCoreDir  = $Script:MSIXCorePath
        $msixmgrPath  = Join-Path $msixCoreDir "x64\msixmgr.exe"
        $versionFile  = Join-Path $msixCoreDir "version.txt"
        $releasesUrl  = "https://api.github.com/repos/microsoft/msix-packaging/releases"

        $installedVersion = $null
        if (Test-Path $versionFile) {
            $installedVersion = (Get-Content $versionFile -Raw).Trim()
        }

        Write-Verbose "Querying GitHub for latest MSIX Core release..."
        try {
            $response      = Invoke-WebRequest -Uri $releasesUrl -UseBasicParsing -Headers @{ 'User-Agent' = 'MSIXForcelets' }
            $releases      = $response.Content | ConvertFrom-Json
            $latestRelease = $releases | Where-Object { $_.tag_name -like 'MSIX-Core-*' } | Select-Object -First 1
        }
        catch {
            Write-Error "Could not query GitHub for MSIX Core releases: $_"
            $latestRelease = $null
        }

        if ($null -ne $latestRelease) {
            $latestTag     = $latestRelease.tag_name
            $msixmgrExists = Test-Path $msixmgrPath

            if ($msixmgrExists -and $installedVersion -eq $latestTag) {
                Write-Host "MSIX Core is up to date ($latestTag)." -ForegroundColor Green
            }
            else {
                if ($msixmgrExists) {
                    Write-Host "MSIX Core update available: $installedVersion -> $latestTag"
                }
                else {
                    Write-Host "MSIX Core not installed. Latest version: $latestTag"
                }

                $proceed = $Force -or $PSCmdlet.ShouldContinue(
                    "Download and install MSIX Core $latestTag from GitHub?", "Install MSIX Core")

                if ($proceed) {
                    $downloadUrl = "https://github.com/microsoft/msix-packaging/releases/download/$latestTag/msixmgr.zip"
                    $tempZip     = Join-Path $env:TEMP ("msixmgr_" + [System.Guid]::NewGuid().ToString('N') + ".zip")
                    $tempExtract = Join-Path $env:TEMP ("msixmgr_" + [System.Guid]::NewGuid().ToString('N'))
                    try {
                        Write-Verbose "Downloading $downloadUrl..."
                        Invoke-WebRequest -Uri $downloadUrl -OutFile $tempZip -UseBasicParsing
                        [System.IO.Compression.ZipFile]::ExtractToDirectory($tempZip, $tempExtract)
                        if (-not (Test-Path $msixCoreDir)) {
                            New-Item -Path $msixCoreDir -ItemType Directory -Force | Out-Null
                        }
                        Copy-Item -Path (Join-Path $tempExtract "*") -Destination $msixCoreDir -Recurse -Force
                        Set-Content -Path $versionFile -Value $latestTag -Encoding UTF8
                        Write-Host "MSIX Core $latestTag installed to $msixCoreDir" -ForegroundColor Green
                        if (Get-Alias msixmgr -ErrorAction SilentlyContinue) {
                            Remove-Item -Path Alias:msixmgr -Force
                        }
                        New-Alias -Name msixmgr -Value $msixmgrPath -Scope Script
                    }
                    catch {
                        Write-Error "Failed to download MSIX Core: $_"
                    }
                    finally {
                        if (Test-Path $tempZip)     { Remove-Item $tempZip     -Force -ErrorAction SilentlyContinue }
                        if (Test-Path $tempExtract) { Remove-Item $tempExtract -Recurse -Force -ErrorAction SilentlyContinue }
                    }
                }
            }
        }
    }

    # --- MSIX Toolkit: makeappx.exe + signtool.exe ---
    if (-not $SkipPackaging) {
        # $Script:MSIXPackagingPath = ..\Libs\MSIXPackaging\WindowsSDK\11\10.0.22000.0\x64
        # Four levels up reaches MSIXPackaging
        $toolkitRoot = Split-Path $Script:MSIXPackagingPath -Parent |
                       Split-Path -Parent | Split-Path -Parent | Split-Path -Parent
        $versionFile = Join-Path $toolkitRoot "version.txt"
        $releasesUrl = "https://api.github.com/repos/microsoft/MSIX-Toolkit/releases"

        $installedVersion = $null
        if (Test-Path $versionFile) {
            $installedVersion = (Get-Content $versionFile -Raw).Trim()
        }

        Write-Verbose "Querying GitHub for latest MSIX Toolkit release..."
        try {
            $response      = Invoke-WebRequest -Uri $releasesUrl -UseBasicParsing -Headers @{ 'User-Agent' = 'MSIXForcelets' }
            $releases      = $response.Content | ConvertFrom-Json
            $latestRelease = $releases | Select-Object -First 1
        }
        catch {
            Write-Error "Could not query GitHub for MSIX Toolkit releases: $_"
            $latestRelease = $null
        }

        if ($null -ne $latestRelease) {
            $latestTag      = $latestRelease.tag_name
            $makeappxExists = Test-Path (Join-Path $Script:MSIXPackagingPath 'makeappx.exe')

            if ($makeappxExists -and $installedVersion -eq $latestTag) {
                Write-Host "MSIX Toolkit is up to date ($latestTag)." -ForegroundColor Green
            }
            else {
                if ($makeappxExists) {
                    Write-Host "MSIX Toolkit update available: $installedVersion -> $latestTag"
                }
                else {
                    Write-Host "MSIX Toolkit not installed. Latest version: $latestTag"
                }

                $proceed = $Force -or $PSCmdlet.ShouldContinue(
                    "Download and install MSIX Toolkit $latestTag from GitHub?", "Install MSIX Toolkit")

                if ($proceed) {
                    # Source archive URL — the toolkit has no separate binary release asset
                    $downloadUrl = "https://github.com/microsoft/MSIX-Toolkit/archive/refs/tags/$($latestTag).zip"
                    $tempZip     = Join-Path $env:TEMP ("MSIXToolkit_" + [System.Guid]::NewGuid().ToString('N') + ".zip")
                    $tempExtract = Join-Path $env:TEMP ("MSIXToolkit_" + [System.Guid]::NewGuid().ToString('N'))
                    try {
                        Write-Verbose "Downloading $downloadUrl..."
                        Invoke-WebRequest -Uri $downloadUrl -OutFile $tempZip -UseBasicParsing
                        [System.IO.Compression.ZipFile]::ExtractToDirectory($tempZip, $tempExtract)

                        # Source archive unpacks to MSIX-Toolkit-{version}\ (no leading 'v')
                        $innerFolder = Join-Path $tempExtract ("MSIX-Toolkit-" + $latestTag.TrimStart('v'))
                        if (-not (Test-Path $innerFolder)) {
                            # Fallback: first subfolder inside the extract dir
                            $innerFolder = (Get-ChildItem $tempExtract -Directory | Select-Object -First 1).FullName
                        }

                        if (-not (Test-Path $toolkitRoot)) {
                            New-Item -Path $toolkitRoot -ItemType Directory -Force | Out-Null
                        }

                        # Copy only the needed subfolders and the license file
                        foreach ($item in @('Scripts', 'WindowsSDK', 'LICENSE')) {
                            $src = Join-Path $innerFolder $item
                            if (Test-Path $src) {
                                Copy-Item -Path $src -Destination $toolkitRoot -Recurse -Force
                            }
                            else {
                                Write-Verbose "Item not found in archive: $item"
                            }
                        }

                        Set-Content -Path $versionFile -Value $latestTag -Encoding UTF8
                        Write-Host "MSIX Toolkit $latestTag installed to $toolkitRoot" -ForegroundColor Green

                        # Refresh aliases for the current session
                        $makeappxExe = Join-Path $Script:MSIXPackagingPath 'makeappx.exe'
                        if (Test-Path $makeappxExe) {
                            if (Get-Alias makeappx -ErrorAction SilentlyContinue) { Remove-Item Alias:makeappx -Force }
                            if (Get-Alias signtool -ErrorAction SilentlyContinue) { Remove-Item Alias:signtool -Force }
                            New-Alias -Name makeappx -Value $makeappxExe -Scope Global
                            New-Alias -Name signtool -Value (Join-Path $Script:MSIXPackagingPath 'signtool.exe') -Scope Global
                        }
                        else {
                            Write-Warning "makeappx.exe not found at expected path after extraction: $($Script:MSIXPackagingPath)"
                        }
                    }
                    catch {
                        Write-Error "Failed to download MSIX Toolkit: $_"
                    }
                    finally {
                        if (Test-Path $tempZip)     { Remove-Item $tempZip     -Force -ErrorAction SilentlyContinue }
                        if (Test-Path $tempExtract) { Remove-Item $tempExtract -Recurse -Force -ErrorAction SilentlyContinue }
                    }
                }
            }
        }
    }
}