Skip to content

Commit 4b99986

Browse files
committed
Update ci.yml
1 parent 641e097 commit 4b99986

File tree

6 files changed

+333
-54
lines changed

6 files changed

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

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

Lines changed: 23 additions & 16 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,39 @@ jobs:
119118
- name: Build & package Files
120119
shell: pwsh
121120
run: |
122-
msbuild "$env:PACKAGE_PROJECT_PATH" `
121+
msbuild "$env:APP_PROJECT_PATH" `
123122
-t:Build `
124-
-t:_GenerateAppxPackage `
125123
-p:Platform=$env:PLATFORM `
126124
-p:Configuration=$env:CONFIGURATION `
127125
-p:AppxBundlePlatforms=$env:APPX_BUNDLE_PLATFORMS `
128126
-p:AppxPackageDir="$env:APPX_PACKAGE_DIR" `
129127
-p:AppxBundle=Always `
128+
-p:GenerateAppxPackageOnBuild=true `
130129
-p:UapAppxPackageBuildMode=Sideload `
131-
-p:GenerateAppInstallerFile=True `
132-
-p:AppInstallerUri=$env:APP_INSTALLER_SIDELOAD_URL `
133130
-v:quiet
134131
132+
- name: Get package version
133+
id: get-version
134+
shell: pwsh
135+
run: |
136+
[xml]$manifest = Get-Content "$env:PACKAGE_MANIFEST_PATH"
137+
$version = $manifest.Package.Identity.Version
138+
echo "VERSION=$version" >> $env:GITHUB_OUTPUT
139+
140+
- name: Create msixbundle and appinstaller
141+
shell: pwsh
142+
run: |
143+
. './.github/scripts/Create-MsixBundle.ps1' `
144+
-AppxPackageDir "$env:APPX_PACKAGE_DIR" `
145+
-BundleName "Files.Package" `
146+
-Version "${{ steps.get-version.outputs.VERSION }}" `
147+
-AppInstallerUri "$env:APP_INSTALLER_SIDELOAD_URL" `
148+
-BuildMode "Sideload"
149+
135150
- name: Remove empty files from the packages
136151
shell: bash
137152
run: find $ARTIFACTS_STAGING_DIR -empty -delete
138153

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-
147154
- name: Sign Files with Azure Trusted Signing
148155
uses: azure/trusted-signing-action@v0.4.0
149156
with:

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

Lines changed: 23 additions & 16 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,39 @@ jobs:
119118
- name: Build & package Files
120119
shell: pwsh
121120
run: |
122-
msbuild "$env:PACKAGE_PROJECT_PATH" `
121+
msbuild "$env:APP_PROJECT_PATH" `
123122
-t:Build `
124-
-t:_GenerateAppxPackage `
125123
-p:Platform=$env:PLATFORM `
126124
-p:Configuration=$env:CONFIGURATION `
127125
-p:AppxBundlePlatforms=$env:APPX_BUNDLE_PLATFORMS `
128126
-p:AppxPackageDir="$env:APPX_PACKAGE_DIR" `
129127
-p:AppxBundle=Always `
128+
-p:GenerateAppxPackageOnBuild=true `
130129
-p:UapAppxPackageBuildMode=Sideload `
131-
-p:GenerateAppInstallerFile=True `
132-
-p:AppInstallerUri=$env:APP_INSTALLER_SIDELOAD_URL `
133130
-v:quiet
134131
132+
- name: Get package version
133+
id: get-version
134+
shell: pwsh
135+
run: |
136+
[xml]$manifest = Get-Content "$env:PACKAGE_MANIFEST_PATH"
137+
$version = $manifest.Package.Identity.Version
138+
echo "VERSION=$version" >> $env:GITHUB_OUTPUT
139+
140+
- name: Create msixbundle and appinstaller
141+
shell: pwsh
142+
run: |
143+
. './.github/scripts/Create-MsixBundle.ps1' `
144+
-AppxPackageDir "$env:APPX_PACKAGE_DIR" `
145+
-BundleName "Files.Package" `
146+
-Version "${{ steps.get-version.outputs.VERSION }}" `
147+
-AppInstallerUri "$env:APP_INSTALLER_SIDELOAD_URL" `
148+
-BuildMode "Sideload"
149+
135150
- name: Remove empty files from the packages
136151
shell: bash
137152
run: find $ARTIFACTS_STAGING_DIR -empty -delete
138153

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-
147154
- name: Sign Files with Azure Trusted Signing
148155
uses: azure/trusted-signing-action@v0.4.0
149156
with:

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

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,9 @@ jobs:
3131
APPX_BUNDLE_PLATFORMS: 'x64|arm64'
3232
WORKING_DIR: '${{ github.workspace }}' # D:\a\Files\Files\
3333
ARTIFACTS_STAGING_DIR: '${{ github.workspace }}\artifacts'
34-
APPX_PACKAGE_DIR: '${{ github.workspace }}\artifacts\AppxPackages'
35-
PACKAGE_PROJECT_DIR: '${{ github.workspace }}\src\Files.App (Package)'
36-
PACKAGE_PROJECT_PATH: '${{ github.workspace }}\src\Files.App (Package)\Files.Package.wapproj'
37-
PACKAGE_MANIFEST_PATH: '${{ github.workspace }}\src\Files.App (Package)\Package.appxmanifest'
34+
APPX_PACKAGE_DIR: '${{ github.workspace }}\artifacts\AppxPackages\'
35+
APP_PROJECT_PATH: '${{ github.workspace }}\src\Files.App\Files.App.csproj'
36+
PACKAGE_MANIFEST_PATH: '${{ github.workspace }}\src\Files.App\Package.appxmanifest'
3837
LAUNCHER_PROJECT_PATH: 'src\Files.App.Launcher\Files.App.Launcher.vcxproj'
3938

4039
steps:
@@ -116,17 +115,34 @@ jobs:
116115
- name: Build & package Files
117116
shell: pwsh
118117
run: |
119-
msbuild "$env:PACKAGE_PROJECT_PATH" `
118+
msbuild "$env:APP_PROJECT_PATH" `
120119
-t:Build `
121-
-t:_GenerateAppxPackage `
122120
-p:Platform=$env:PLATFORM `
123121
-p:Configuration=$env:CONFIGURATION `
124122
-p:AppxBundlePlatforms=$env:APPX_BUNDLE_PLATFORMS `
125123
-p:AppxPackageDir="$env:APPX_PACKAGE_DIR" `
126124
-p:AppxBundle=Always `
125+
-p:GenerateAppxPackageOnBuild=true `
127126
-p:UapAppxPackageBuildMode=StoreUpload `
128127
-v:quiet
129128
129+
- name: Get package version
130+
id: get-version
131+
shell: pwsh
132+
run: |
133+
[xml]$manifest = Get-Content "$env:PACKAGE_MANIFEST_PATH"
134+
$version = $manifest.Package.Identity.Version
135+
echo "VERSION=$version" >> $env:GITHUB_OUTPUT
136+
137+
- name: Create msixbundle and msixupload
138+
shell: pwsh
139+
run: |
140+
. './.github/scripts/Create-MsixBundle.ps1' `
141+
-AppxPackageDir "$env:APPX_PACKAGE_DIR" `
142+
-BundleName "Files" `
143+
-Version "${{ steps.get-version.outputs.VERSION }}" `
144+
-BuildMode "StoreUpload"
145+
130146
- name: Remove empty files from the packages
131147
shell: bash
132148
run: find $ARTIFACTS_STAGING_DIR -empty -delete

0 commit comments

Comments
 (0)