Skip to content

Commit 2c30234

Browse files
committed
Update ci.yml
1 parent 641e097 commit 2c30234

File tree

6 files changed

+390
-86
lines changed

6 files changed

+390
-86
lines changed
Lines changed: 206 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,206 @@
1+
# Copyright (c) Files Community
2+
# Licensed under the MIT License.
3+
4+
# Abstract:
5+
# Creates an .msixbundle from individual per-platform .msix packages produced
6+
# by single-project MSIX packaging. Optionally generates an .appinstaller file
7+
# for sideload deployments, and an .msixupload for Store submissions.
8+
9+
param(
10+
[Parameter(Mandatory)]
11+
[string]$AppxPackageDir,
12+
13+
[Parameter(Mandatory)]
14+
[string]$BundleName,
15+
16+
[Parameter(Mandatory)]
17+
[string]$Version,
18+
19+
[string]$AppInstallerUri = "",
20+
21+
[ValidateSet("Sideload", "StoreUpload", "SideloadOnly")]
22+
[string]$BuildMode = "Sideload"
23+
)
24+
25+
$ErrorActionPreference = "Stop"
26+
27+
# Locate makeappx.exe from the Windows 10 SDK
28+
$sdkRoot = "${env:ProgramFiles(x86)}\Windows Kits\10\bin"
29+
$makeAppx = Get-ChildItem -Path $sdkRoot -Filter "makeappx.exe" -Recurse -ErrorAction SilentlyContinue |
30+
Where-Object { $_.DirectoryName -match '\\x64$' } |
31+
Sort-Object { [version]($_.DirectoryName -replace '^.*\\bin\\([^\\]+)\\.*$','$1') } -Descending |
32+
Select-Object -First 1
33+
if ($null -eq $makeAppx) {
34+
Write-Error "makeappx.exe not found under '$sdkRoot'"
35+
exit 1
36+
}
37+
Write-Host "Using makeappx: $($makeAppx.FullName)"
38+
39+
# Find only the app .msix files (exclude dependency packages)
40+
$msixFiles = Get-ChildItem -Path $AppxPackageDir -Filter "*.msix" -Recurse |
41+
Where-Object { $_.DirectoryName -notmatch '\\Dependencies\\' }
42+
if ($msixFiles.Count -eq 0) {
43+
Write-Error "No .msix files found in '$AppxPackageDir'"
44+
exit 1
45+
}
46+
47+
Write-Host "Found $($msixFiles.Count) .msix package(s):"
48+
$msixFiles | ForEach-Object { Write-Host " $_" }
49+
50+
# Create a temporary mapping file for MakeAppx
51+
$mappingDir = Join-Path $AppxPackageDir "_bundletemp"
52+
if (Test-Path $mappingDir) { Remove-Item $mappingDir -Recurse -Force }
53+
New-Item -ItemType Directory -Path $mappingDir | Out-Null
54+
55+
# Copy all msix files into the flat temp directory
56+
foreach ($msix in $msixFiles) {
57+
Copy-Item $msix.FullName -Destination $mappingDir
58+
}
59+
60+
# Output bundle path
61+
$bundlePath = Join-Path $AppxPackageDir "$BundleName.msixbundle"
62+
if (Test-Path $bundlePath) { Remove-Item $bundlePath -Force }
63+
64+
# Use MakeAppx to create the bundle
65+
Write-Host "Creating msixbundle at: $bundlePath"
66+
& $makeAppx.FullName bundle /d $mappingDir /p $bundlePath /o
67+
if ($LASTEXITCODE -ne 0) {
68+
Write-Error "MakeAppx bundle creation failed with exit code $LASTEXITCODE"
69+
exit 1
70+
}
71+
72+
# Clean up temp directory
73+
Remove-Item $mappingDir -Recurse -Force
74+
75+
Write-Host "Successfully created: $bundlePath"
76+
77+
# Generate .msixupload for Store submissions
78+
if ($BuildMode -eq "StoreUpload") {
79+
$uploadPath = Join-Path $AppxPackageDir "$BundleName.msixupload"
80+
if (Test-Path $uploadPath) { Remove-Item $uploadPath -Force }
81+
82+
Write-Host "Creating msixupload at: $uploadPath"
83+
Compress-Archive -Path $bundlePath -DestinationPath $uploadPath -Force
84+
# Rename .zip to .msixupload
85+
if ($uploadPath -notlike "*.msixupload") {
86+
Rename-Item "$uploadPath" -NewName $uploadPath
87+
}
88+
Write-Host "Successfully created: $uploadPath"
89+
}
90+
91+
# Generate .appinstaller for sideload deployments
92+
if ($AppInstallerUri -ne "" -and ($BuildMode -eq "Sideload" -or $BuildMode -eq "SideloadOnly")) {
93+
$appInstallerPath = Join-Path $AppxPackageDir "$BundleName.appinstaller"
94+
95+
# Extract dependencies from the built .msix packages by reading their AppxManifest.xml
96+
# Each .msix is a ZIP containing AppxManifest.xml with <PackageDependency> entries
97+
$dependencyMap = @{} # Key: "Name|Publisher" -> @{ Name; Publisher; MinVersion; Architectures }
98+
99+
foreach ($msix in $msixFiles) {
100+
$extractDir = Join-Path $AppxPackageDir "_extract_$($msix.BaseName)"
101+
if (Test-Path $extractDir) { Remove-Item $extractDir -Recurse -Force }
102+
103+
# Extract only AppxManifest.xml from the msix (which is a ZIP)
104+
Add-Type -AssemblyName System.IO.Compression.FileSystem
105+
$zip = [System.IO.Compression.ZipFile]::OpenRead($msix.FullName)
106+
try {
107+
$manifestEntry = $zip.Entries | Where-Object { $_.FullName -eq "AppxManifest.xml" } | Select-Object -First 1
108+
if ($null -eq $manifestEntry) {
109+
Write-Warning "No AppxManifest.xml found in $($msix.Name), skipping dependency extraction"
110+
continue
111+
}
112+
113+
$reader = New-Object System.IO.StreamReader($manifestEntry.Open())
114+
[xml]$msixManifest = $reader.ReadToEnd()
115+
$reader.Close()
116+
}
117+
finally {
118+
$zip.Dispose()
119+
}
120+
121+
# Determine architecture from the manifest Identity
122+
$ns = @{ pkg = "http://schemas.microsoft.com/appx/manifest/foundation/windows10" }
123+
$identity = $msixManifest.SelectSingleNode("/pkg:Package/pkg:Identity", (New-Object System.Xml.XmlNamespaceManager($msixManifest.NameTable)))
124+
if ($null -eq $identity) {
125+
# Try without namespace
126+
$arch = "x64"
127+
} else {
128+
$nsmgr = New-Object System.Xml.XmlNamespaceManager($msixManifest.NameTable)
129+
$nsmgr.AddNamespace("pkg", "http://schemas.microsoft.com/appx/manifest/foundation/windows10")
130+
$identityNode = $msixManifest.SelectSingleNode("/pkg:Package/pkg:Identity", $nsmgr)
131+
$arch = $identityNode.GetAttribute("ProcessorArchitecture")
132+
if ([string]::IsNullOrEmpty($arch)) { $arch = "x64" }
133+
}
134+
135+
# Parse PackageDependency entries
136+
$nsmgr = New-Object System.Xml.XmlNamespaceManager($msixManifest.NameTable)
137+
$nsmgr.AddNamespace("pkg", "http://schemas.microsoft.com/appx/manifest/foundation/windows10")
138+
$deps = $msixManifest.SelectNodes("/pkg:Package/pkg:Dependencies/pkg:PackageDependency", $nsmgr)
139+
140+
foreach ($dep in $deps) {
141+
$depName = $dep.GetAttribute("Name")
142+
$depPublisher = $dep.GetAttribute("Publisher")
143+
$depMinVersion = $dep.GetAttribute("MinVersion")
144+
$key = "$depName|$depPublisher"
145+
146+
if (-not $dependencyMap.ContainsKey($key)) {
147+
$dependencyMap[$key] = @{
148+
Name = $depName
149+
Publisher = $depPublisher
150+
MinVersion = $depMinVersion
151+
Architectures = [System.Collections.Generic.HashSet[string]]::new()
152+
}
153+
}
154+
[void]$dependencyMap[$key].Architectures.Add($arch)
155+
156+
# Keep the highest MinVersion seen
157+
if ([version]$depMinVersion -gt [version]$dependencyMap[$key].MinVersion) {
158+
$dependencyMap[$key].MinVersion = $depMinVersion
159+
}
160+
}
161+
}
162+
163+
# Build dependency XML entries
164+
$dependencyEntries = ""
165+
foreach ($dep in $dependencyMap.Values) {
166+
foreach ($arch in $dep.Architectures) {
167+
$dependencyEntries += @"
168+
169+
<Package
170+
Name="$($dep.Name)"
171+
Publisher="$($dep.Publisher)"
172+
Version="$($dep.MinVersion)"
173+
ProcessorArchitecture="$arch" />
174+
"@
175+
}
176+
}
177+
178+
if ($dependencyEntries -eq "") {
179+
Write-Warning "No package dependencies found in .msix manifests"
180+
} else {
181+
Write-Host "Discovered dependencies:$dependencyEntries"
182+
}
183+
184+
$appInstallerContent = @"
185+
<?xml version="1.0" encoding="utf-8"?>
186+
<AppInstaller
187+
xmlns="http://schemas.microsoft.com/appx/appinstaller/2018"
188+
Uri="$AppInstallerUri$BundleName.appinstaller"
189+
Version="$Version">
190+
<MainBundle
191+
Name="$BundleName"
192+
Version="$Version"
193+
Uri="$AppInstallerUri$BundleName.msixbundle" />
194+
<Dependencies>$dependencyEntries
195+
</Dependencies>
196+
<UpdateSettings>
197+
<OnLaunch HoursBetweenUpdateChecks="0" />
198+
<AutomaticBackgroundTask />
199+
</UpdateSettings>
200+
</AppInstaller>
201+
"@
202+
203+
Write-Host "Creating appinstaller at: $appInstallerPath"
204+
$appInstallerContent | Set-Content -Path $appInstallerPath -Encoding UTF8
205+
Write-Host "Successfully created: $appInstallerPath"
206+
}

.github/workflows/cd-sideload-preview.yml

Lines changed: 34 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,9 @@ jobs:
3232
APPX_BUNDLE_PLATFORMS: 'x64|arm64'
3333
WORKING_DIR: '${{ github.workspace }}' # D:\a\Files\Files\
3434
ARTIFACTS_STAGING_DIR: '${{ github.workspace }}\artifacts'
35-
APPX_PACKAGE_DIR: '${{ github.workspace }}\artifacts\AppxPackages'
36-
PACKAGE_PROJECT_DIR: 'src\Files.App (Package)'
37-
PACKAGE_PROJECT_PATH: 'src\Files.App (Package)\Files.Package.wapproj'
38-
PACKAGE_MANIFEST_PATH: 'src\Files.App (Package)\Package.appxmanifest'
35+
APPX_PACKAGE_DIR: '${{ github.workspace }}\artifacts\AppxPackages\'
36+
APP_PROJECT_PATH: 'src\Files.App\Files.App.csproj'
37+
PACKAGE_MANIFEST_PATH: 'src\Files.App\Package.appxmanifest'
3938
LAUNCHER_PROJECT_PATH: 'src\Files.App.Launcher\Files.App.Launcher.vcxproj'
4039
TEST_PROJECT_PATH: 'tests\Files.InteractionTests\Files.InteractionTests.csproj'
4140
APP_INSTALLER_SIDELOAD_URL: 'https://cdn.files.community/files/preview/'
@@ -119,31 +118,42 @@ jobs:
119118
- name: Build & package Files
120119
shell: pwsh
121120
run: |
122-
msbuild "$env:PACKAGE_PROJECT_PATH" `
123-
-t:Build `
124-
-t:_GenerateAppxPackage `
125-
-p:Platform=$env:PLATFORM `
126-
-p:Configuration=$env:CONFIGURATION `
127-
-p:AppxBundlePlatforms=$env:APPX_BUNDLE_PLATFORMS `
128-
-p:AppxPackageDir="$env:APPX_PACKAGE_DIR" `
129-
-p:AppxBundle=Always `
130-
-p:UapAppxPackageBuildMode=Sideload `
131-
-p:GenerateAppInstallerFile=True `
132-
-p:AppInstallerUri=$env:APP_INSTALLER_SIDELOAD_URL `
133-
-v:quiet
121+
$platforms = "$env:APPX_BUNDLE_PLATFORMS" -split '\|'
122+
foreach ($plat in $platforms) {
123+
Write-Host "Building for $plat..."
124+
msbuild "$env:APP_PROJECT_PATH" `
125+
-t:Build `
126+
-p:Platform=$plat `
127+
-p:Configuration=$env:CONFIGURATION `
128+
-p:AppxPackageDir="$env:APPX_PACKAGE_DIR" `
129+
-p:AppxBundle=Never `
130+
-p:GenerateAppxPackageOnBuild=true `
131+
-p:UapAppxPackageBuildMode=Sideload `
132+
-v:quiet
133+
}
134+
135+
- name: Get package version
136+
id: get-version
137+
shell: pwsh
138+
run: |
139+
[xml]$manifest = Get-Content "$env:PACKAGE_MANIFEST_PATH"
140+
$version = $manifest.Package.Identity.Version
141+
echo "VERSION=$version" >> $env:GITHUB_OUTPUT
142+
143+
- name: Create msixbundle and appinstaller
144+
shell: pwsh
145+
run: |
146+
. './.github/scripts/Create-MsixBundle.ps1' `
147+
-AppxPackageDir "$env:APPX_PACKAGE_DIR" `
148+
-BundleName "Files.Package" `
149+
-Version "${{ steps.get-version.outputs.VERSION }}" `
150+
-AppInstallerUri "$env:APP_INSTALLER_SIDELOAD_URL" `
151+
-BuildMode "Sideload"
134152
135153
- name: Remove empty files from the packages
136154
shell: bash
137155
run: find $ARTIFACTS_STAGING_DIR -empty -delete
138156

139-
- name: Update appinstaller schema
140-
run: |
141-
$newSchema = "http://schemas.microsoft.com/appx/appinstaller/2018"
142-
$localFilePath = "${{ env.APPX_PACKAGE_DIR }}/Files.Package.appinstaller"
143-
$fileContent = Get-Content $localFilePath
144-
$fileContent = $fileContent.Replace("http://schemas.microsoft.com/appx/appinstaller/2017/2", $newSchema)
145-
$fileContent | Set-Content $localFilePath
146-
147157
- name: Sign Files with Azure Trusted Signing
148158
uses: azure/trusted-signing-action@v0.4.0
149159
with:

.github/workflows/cd-sideload-stable.yml

Lines changed: 34 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,9 @@ jobs:
3232
APPX_BUNDLE_PLATFORMS: 'x64|arm64'
3333
WORKING_DIR: '${{ github.workspace }}' # D:\a\Files\Files\
3434
ARTIFACTS_STAGING_DIR: '${{ github.workspace }}\artifacts'
35-
APPX_PACKAGE_DIR: '${{ github.workspace }}\artifacts\AppxPackages'
36-
PACKAGE_PROJECT_DIR: 'src\Files.App (Package)'
37-
PACKAGE_PROJECT_PATH: 'src\Files.App (Package)\Files.Package.wapproj'
38-
PACKAGE_MANIFEST_PATH: 'src\Files.App (Package)\Package.appxmanifest'
35+
APPX_PACKAGE_DIR: '${{ github.workspace }}\artifacts\AppxPackages\'
36+
APP_PROJECT_PATH: 'src\Files.App\Files.App.csproj'
37+
PACKAGE_MANIFEST_PATH: 'src\Files.App\Package.appxmanifest'
3938
LAUNCHER_PROJECT_PATH: 'src\Files.App.Launcher\Files.App.Launcher.vcxproj'
4039
TEST_PROJECT_PATH: 'tests\Files.InteractionTests\Files.InteractionTests.csproj'
4140
APP_INSTALLER_SIDELOAD_URL: 'https://cdn.files.community/files/stable/'
@@ -119,31 +118,42 @@ jobs:
119118
- name: Build & package Files
120119
shell: pwsh
121120
run: |
122-
msbuild "$env:PACKAGE_PROJECT_PATH" `
123-
-t:Build `
124-
-t:_GenerateAppxPackage `
125-
-p:Platform=$env:PLATFORM `
126-
-p:Configuration=$env:CONFIGURATION `
127-
-p:AppxBundlePlatforms=$env:APPX_BUNDLE_PLATFORMS `
128-
-p:AppxPackageDir="$env:APPX_PACKAGE_DIR" `
129-
-p:AppxBundle=Always `
130-
-p:UapAppxPackageBuildMode=Sideload `
131-
-p:GenerateAppInstallerFile=True `
132-
-p:AppInstallerUri=$env:APP_INSTALLER_SIDELOAD_URL `
133-
-v:quiet
121+
$platforms = "$env:APPX_BUNDLE_PLATFORMS" -split '\|'
122+
foreach ($plat in $platforms) {
123+
Write-Host "Building for $plat..."
124+
msbuild "$env:APP_PROJECT_PATH" `
125+
-t:Build `
126+
-p:Platform=$plat `
127+
-p:Configuration=$env:CONFIGURATION `
128+
-p:AppxPackageDir="$env:APPX_PACKAGE_DIR" `
129+
-p:AppxBundle=Never `
130+
-p:GenerateAppxPackageOnBuild=true `
131+
-p:UapAppxPackageBuildMode=Sideload `
132+
-v:quiet
133+
}
134+
135+
- name: Get package version
136+
id: get-version
137+
shell: pwsh
138+
run: |
139+
[xml]$manifest = Get-Content "$env:PACKAGE_MANIFEST_PATH"
140+
$version = $manifest.Package.Identity.Version
141+
echo "VERSION=$version" >> $env:GITHUB_OUTPUT
142+
143+
- name: Create msixbundle and appinstaller
144+
shell: pwsh
145+
run: |
146+
. './.github/scripts/Create-MsixBundle.ps1' `
147+
-AppxPackageDir "$env:APPX_PACKAGE_DIR" `
148+
-BundleName "Files.Package" `
149+
-Version "${{ steps.get-version.outputs.VERSION }}" `
150+
-AppInstallerUri "$env:APP_INSTALLER_SIDELOAD_URL" `
151+
-BuildMode "Sideload"
134152
135153
- name: Remove empty files from the packages
136154
shell: bash
137155
run: find $ARTIFACTS_STAGING_DIR -empty -delete
138156

139-
- name: Update appinstaller schema
140-
run: |
141-
$newSchema = "http://schemas.microsoft.com/appx/appinstaller/2018"
142-
$localFilePath = "${{ env.APPX_PACKAGE_DIR }}/Files.Package.appinstaller"
143-
$fileContent = Get-Content $localFilePath
144-
$fileContent = $fileContent.Replace("http://schemas.microsoft.com/appx/appinstaller/2017/2", $newSchema)
145-
$fileContent | Set-Content $localFilePath
146-
147157
- name: Sign Files with Azure Trusted Signing
148158
uses: azure/trusted-signing-action@v0.4.0
149159
with:

0 commit comments

Comments
 (0)