Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
20 changes: 16 additions & 4 deletions .github/scripts/Configure-AppxManifest.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ if ($Branch -eq "SideloadPreview")
# Save modified Package.appxmanifest
$xmlDoc.Save($PackageManifestPath)

Get-ChildItem $WorkingDir -Include *.csproj, *.appxmanifest, *.wapproj, *.xaml -recurse | ForEach-Object -Process `
Get-ChildItem $WorkingDir -Include *.csproj, *.appxmanifest, *.xaml -recurse | ForEach-Object -Process `
{ `
(Get-Content $_ -Raw | ForEach-Object -Process { $_ -replace "Assets\\AppTiles\\Dev", "Assets\AppTiles\Preview" }) | `
Set-Content $_ -NoNewline `
Expand All @@ -53,6 +53,7 @@ if ($Branch -eq "SideloadPreview")
(Get-Content $_ -Raw | ForEach-Object -Process { $_ -replace "files-dev", "files-preview" }) | `
Set-Content $_ -NoNewline `
}

}
elseif ($Branch -eq "StorePreview")
{
Expand All @@ -75,7 +76,7 @@ elseif ($Branch -eq "StorePreview")

$xmlDoc.Save($PackageManifestPath)

Get-ChildItem $WorkingDir -Include *.csproj, *.appxmanifest, *.wapproj, *.xaml -recurse | ForEach-Object -Process `
Get-ChildItem $WorkingDir -Include *.csproj, *.appxmanifest, *.xaml -recurse | ForEach-Object -Process `
{ `
(Get-Content $_ -Raw | ForEach-Object -Process { $_ -replace "Assets\\AppTiles\\Dev", "Assets\AppTiles\Preview" }) | `
Set-Content $_ -NoNewline `
Expand All @@ -86,6 +87,7 @@ elseif ($Branch -eq "StorePreview")
(Get-Content $_ -Raw | ForEach-Object -Process { $_ -replace "files-dev", "files-preview" }) | `
Set-Content $_ -NoNewline `
}

}
elseif ($Branch -eq "SideloadStable")
{
Expand All @@ -102,7 +104,7 @@ elseif ($Branch -eq "SideloadStable")
# Save modified Package.appxmanifest
$xmlDoc.Save($PackageManifestPath)

Get-ChildItem $WorkingDir -Include *.csproj, *.appxmanifest, *.wapproj, *.xaml -recurse | ForEach-Object -Process `
Get-ChildItem $WorkingDir -Include *.csproj, *.appxmanifest, *.xaml -recurse | ForEach-Object -Process `
{ `
(Get-Content $_ -Raw | ForEach-Object -Process { $_ -replace "Assets\\AppTiles\\Dev", "Assets\AppTiles\Release" }) | `
Set-Content $_ -NoNewline `
Expand All @@ -113,6 +115,7 @@ elseif ($Branch -eq "SideloadStable")
(Get-Content $_ -Raw | ForEach-Object -Process { $_ -replace "files-dev", "files-stable" }) | `
Set-Content $_ -NoNewline `
}

}
elseif ($Branch -eq "StoreStable")
{
Expand All @@ -133,7 +136,7 @@ elseif ($Branch -eq "StoreStable")
# Save modified Package.appxmanifest
$xmlDoc.Save($PackageManifestPath)

Get-ChildItem $WorkingDir -Include *.csproj, *.appxmanifest, *.wapproj, *.xaml -recurse | ForEach-Object -Process `
Get-ChildItem $WorkingDir -Include *.csproj, *.appxmanifest, *.xaml -recurse | ForEach-Object -Process `
{ `
(Get-Content $_ -Raw | ForEach-Object -Process { $_ -replace "Assets\\AppTiles\\Dev", "Assets\AppTiles\Release" }) | `
Set-Content $_ -NoNewline `
Expand All @@ -144,6 +147,15 @@ elseif ($Branch -eq "StoreStable")
(Get-Content $_ -Raw | ForEach-Object -Process { $_ -replace "files-dev", "files-stable" }) | `
Set-Content $_ -NoNewline `
}

}

# Remove unused tile assets so they don't end up in the package
$keepTiles = if ($Branch -match 'Preview') { 'Preview' } elseif ($Branch -match 'Stable') { 'Release' } else { $null }
foreach ($folder in @('Dev', 'Preview', 'Release')) {
if ($folder -eq $keepTiles) { continue }
$tilePath = Join-Path $WorkingDir "src\Files.App\Assets\AppTiles\$folder"
if (Test-Path $tilePath) { Remove-Item $tilePath -Recurse -Force }
}

Get-ChildItem $WorkingDir -Include *.cs -recurse | ForEach-Object -Process `
Expand Down
275 changes: 275 additions & 0 deletions .github/scripts/Create-MsixBundle.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,275 @@
# Copyright (c) Files Community
# Licensed under the MIT License.

# Creates an .msixbundle from individual per-platform .msix packages produced
# by single-project MSIX packaging. Optionally generates an .appinstaller file
# for sideload deployments, and an .msixupload for Store submissions.

param(
[Parameter(Mandatory)]
[string]$AppxPackageDir,

[Parameter(Mandatory)]
[string]$BundleName,

[string]$Version = "",

[string]$PackageManifestPath = "",

[string]$AppInstallerUri = "",

[ValidateSet("Sideload", "StoreUpload", "SideloadOnly")]
[string]$BuildMode = "Sideload"
)

$ErrorActionPreference = "Stop"

function Ensure-Directory([string]$Path) {
if (-not (Test-Path $Path)) { New-Item -ItemType Directory -Path $Path | Out-Null }
}

# Canonical arch names for the WAP-compatible folder layout
$archMap = @{ 'arm64' = 'ARM64'; 'x64' = 'x64'; 'x86' = 'x86' }

# --- Locate makeappx.exe ---

$sdkRoot = "${env:ProgramFiles(x86)}\Windows Kits\10\bin"
$makeAppx = Get-ChildItem -Path $sdkRoot -Filter "makeappx.exe" -Recurse -ErrorAction SilentlyContinue |
Where-Object { $_.DirectoryName -match '\\x64$' } |
Sort-Object { [version]($_.DirectoryName -replace '^.*\\bin\\([^\\]+)\\.*$','$1') } -Descending |
Select-Object -First 1
if ($null -eq $makeAppx) {
Write-Error "makeappx.exe not found under '$sdkRoot'"
exit 1
}
Write-Host "Using makeappx: $($makeAppx.FullName)"

# --- Discover per-platform .msix packages ---

$msixFiles = Get-ChildItem -Path $AppxPackageDir -Filter "*.msix" -Recurse |
Where-Object { $_.DirectoryName -notmatch '\\Dependencies\\' }
if ($msixFiles.Count -eq 0) {
Write-Error "No .msix files found in '$AppxPackageDir'"
exit 1
}
Write-Host "Found $($msixFiles.Count) .msix package(s):"
$msixFiles | ForEach-Object { Write-Host " $_" }

if ($Version -eq "" -and $PackageManifestPath -ne "" -and (Test-Path $PackageManifestPath)) {
[xml]$versionManifest = Get-Content $PackageManifestPath
$Version = $versionManifest.Package.Identity.Version
Write-Host "Version from manifest: $Version"
}
if ($Version -eq "") {
$Version = $msixFiles[0].BaseName -replace '^[^_]+_([^_]+)_.*$','$1'
Write-Host "Detected version from filename: $Version"
}

$platformList = $msixFiles | ForEach-Object { $_.BaseName -replace '.*_(\w+)$','$1' } | Sort-Object -Descending
$platforms = $platformList -join '_'

# --- Create .msixbundle ---

$mappingDir = Join-Path $AppxPackageDir "_bundletemp"
if (Test-Path $mappingDir) { Remove-Item $mappingDir -Recurse -Force }
New-Item -ItemType Directory -Path $mappingDir | Out-Null
foreach ($msix in $msixFiles) { Copy-Item $msix.FullName -Destination $mappingDir }

$bundlePath = Join-Path $AppxPackageDir "$BundleName.msixbundle"
if (Test-Path $bundlePath) { Remove-Item $bundlePath -Force }

Write-Host "Creating msixbundle at: $bundlePath"
& $makeAppx.FullName bundle /d $mappingDir /p $bundlePath /bv $Version /o
if ($LASTEXITCODE -ne 0) {
Write-Error "MakeAppx bundle creation failed with exit code $LASTEXITCODE"
exit 1
}
Remove-Item $mappingDir -Recurse -Force
Write-Host "Successfully created: $bundlePath"

# --- Reorganize into WAP-compatible folder structure for CDN upload ---
# Target layout: {BundleName}_{Version}_Test/{BundleName}_{Version}_{platforms}.msixbundle
# {BundleName}_{Version}_Test/Dependencies/{arch}/...

$bundleFolder = "${BundleName}_${Version}_Test"
$bundleFolderPath = Join-Path $AppxPackageDir $bundleFolder
Ensure-Directory $bundleFolderPath

$organizedBundleName = "${BundleName}_${Version}_${platforms}.msixbundle"
$organizedBundlePath = Join-Path $bundleFolderPath $organizedBundleName
Move-Item $bundlePath $organizedBundlePath -Force
Write-Host "Moved bundle to: $organizedBundlePath"

# --- Merge dependency folders from each per-platform build ---

$organizedDepsDir = Join-Path $bundleFolderPath "Dependencies"
foreach ($msix in $msixFiles) {
$perPlatDepsDir = Join-Path $msix.DirectoryName "Dependencies"
if (-not (Test-Path $perPlatDepsDir)) { continue }

Get-ChildItem -Path $perPlatDepsDir -Directory | ForEach-Object {
$archName = $_.Name
$canonicalArch = $archMap[$archName]
if (-not $canonicalArch) { return }
if (-not ($platformList -contains $archName)) { return }

$targetArchDir = Join-Path $organizedDepsDir $canonicalArch
Ensure-Directory $targetArchDir
Get-ChildItem -Path $_.FullName -File | ForEach-Object {
$destFile = Join-Path $targetArchDir $_.Name
if (-not (Test-Path $destFile)) {
Copy-Item $_.FullName -Destination $destFile
Write-Host " Copied dependency: $destFile"
}
}
}
}

# --- Add VCLibs framework packages (not produced by single-project builds) ---

$vcLibsSdkBase = "${env:ProgramFiles(x86)}\Microsoft SDKs\Windows Kits\10\ExtensionSDKs"
$vcLibsPackages = @(
@{ SdkFolder = "Microsoft.VCLibs"; FileTemplate = "Microsoft.VCLibs.{0}.14.00.appx" },
@{ SdkFolder = "Microsoft.VCLibs.Desktop"; FileTemplate = "Microsoft.VCLibs.{0}.14.00.Desktop.appx" }
)

foreach ($platform in $platformList) {
$canonicalArch = $archMap[$platform]
if (-not $canonicalArch) { continue }

$targetArchDir = Join-Path $organizedDepsDir $canonicalArch
Ensure-Directory $targetArchDir

foreach ($vcLib in $vcLibsPackages) {
$fileName = $vcLib.FileTemplate -f $canonicalArch
$destPath = Join-Path $targetArchDir $fileName
if (Test-Path $destPath) { continue }

$sdkPath = Join-Path $vcLibsSdkBase "$($vcLib.SdkFolder)\14.0\Appx\Retail\$platform\$fileName"
if (Test-Path $sdkPath) {
Copy-Item $sdkPath -Destination $destPath
Write-Host " Copied VCLibs from SDK: $destPath"
} else {
Write-Host " Downloading $fileName for $platform..."
try {
Invoke-WebRequest -Uri "https://aka.ms/$fileName" -OutFile $destPath -UseBasicParsing
Write-Host " Downloaded VCLibs: $destPath"
} catch {
Write-Warning " Failed to download $fileName for ${platform}: $_"
}
}
}
}

# --- Collect symbol files and clean up per-platform build folders ---

foreach ($msix in $msixFiles) {
Get-ChildItem -Path $msix.DirectoryName -Filter "*.appxsym" -ErrorAction SilentlyContinue | ForEach-Object {
$symPlatform = $_.BaseName -replace '.*_(\w+)$','$1'
$newSymPath = Join-Path $bundleFolderPath "${BundleName}_${Version}_${symPlatform}.appxsym"
Move-Item $_.FullName $newSymPath -Force
Write-Host "Moved symbol file to: $newSymPath"
}
}

Get-ChildItem -Path $AppxPackageDir -Filter "*.msixupload" -ErrorAction SilentlyContinue |
ForEach-Object { Remove-Item $_.FullName -Force; Write-Host "Removed per-platform upload: $($_.Name)" }

foreach ($msix in $msixFiles) {
if (Test-Path $msix.DirectoryName) {
Remove-Item $msix.DirectoryName -Recurse -Force
Write-Host "Removed per-platform dir: $($msix.DirectoryName)"
}
}

# --- Generate .msixupload for Store submissions ---

if ($BuildMode -eq "StoreUpload") {
$uploadName = "${BundleName}_${Version}_${platforms}_bundle.msixupload"
$uploadPath = Join-Path $AppxPackageDir $uploadName
if (Test-Path $uploadPath) { Remove-Item $uploadPath -Force }

Write-Host "Creating msixupload at: $uploadPath"
$zipPath = "$uploadPath.zip"
Compress-Archive -Path $organizedBundlePath -DestinationPath $zipPath -Force
Move-Item $zipPath $uploadPath -Force
Write-Host "Successfully created: $uploadPath"
}

# --- Generate .appinstaller for sideload deployments ---

if ($AppInstallerUri -ne "" -and ($BuildMode -eq "Sideload" -or $BuildMode -eq "SideloadOnly")) {
$appInstallerPath = Join-Path $AppxPackageDir "$BundleName.appinstaller"

if ($PackageManifestPath -ne "" -and (Test-Path $PackageManifestPath)) {
[xml]$manifestXml = Get-Content $PackageManifestPath
$packageName = $manifestXml.Package.Identity.Name
$packagePublisher = $manifestXml.Package.Identity.Publisher
} else {
Write-Warning "PackageManifestPath not provided; falling back to BundleName for identity"
$packageName = $BundleName
$packagePublisher = ""
}

$bundleUri = "${AppInstallerUri}${bundleFolder}/${organizedBundleName}"

# Build dependency XML entries by reading each package's embedded manifest
$dependencyEntries = ""
if (Test-Path $organizedDepsDir) {
Add-Type -AssemblyName System.IO.Compression.FileSystem -ErrorAction SilentlyContinue
Get-ChildItem -Path $organizedDepsDir -Recurse -Include *.appx, *.msix | ForEach-Object {
$depArch = Split-Path (Split-Path $_.FullName -Parent) -Leaf

$depZip = [System.IO.Compression.ZipFile]::OpenRead($_.FullName)
try {
$entry = $depZip.Entries | Where-Object { $_.FullName -eq "AppxManifest.xml" } | Select-Object -First 1
if ($null -eq $entry) { return }
$reader = New-Object System.IO.StreamReader($entry.Open())
[xml]$depManifest = $reader.ReadToEnd()
$reader.Close()
} finally { $depZip.Dispose() }

$nsMgr = New-Object System.Xml.XmlNamespaceManager($depManifest.NameTable)
$nsMgr.AddNamespace("pkg", "http://schemas.microsoft.com/appx/manifest/foundation/windows10")
$id = $depManifest.SelectSingleNode("/pkg:Package/pkg:Identity", $nsMgr)
if ($null -eq $id) { return }

$arch = $id.GetAttribute("ProcessorArchitecture")
if ([string]::IsNullOrEmpty($arch)) { $arch = $depArch }
$depUri = "${AppInstallerUri}${bundleFolder}/Dependencies/$depArch/$($_.Name)"

$dependencyEntries += @"

<Package
Name="$($id.GetAttribute("Name"))"
Publisher="$($id.GetAttribute("Publisher"))"
ProcessorArchitecture="$arch"
Uri="$depUri"
Version="$($id.GetAttribute("Version"))" />
"@
}
}

if ($dependencyEntries -eq "") {
Write-Warning "No dependency packages found"
} else {
Write-Host "Discovered dependencies:$dependencyEntries"
}

@"
<?xml version="1.0" encoding="utf-8"?>
<AppInstaller
Uri="${AppInstallerUri}${BundleName}.appinstaller"
Version="$Version" xmlns="http://schemas.microsoft.com/appx/appinstaller/2018">
<MainBundle
Name="$packageName"
Publisher="$packagePublisher"
Version="$Version"
Uri="$bundleUri" />
<Dependencies>$dependencyEntries
</Dependencies>
</AppInstaller>
"@ | Set-Content -Path $appInstallerPath -Encoding UTF8

Write-Host "Created appinstaller at: $appInstallerPath"
}
Loading
Loading