Skip to content

Build and Publish WinUI3 App #11

Build and Publish WinUI3 App

Build and Publish WinUI3 App #11

name: Build and Upload
on:
workflow_dispatch:
inputs:
version:
description: 'Version number (e.g., 1.0.0.1)'
required: true
type: string
prerelease:
description: 'If pre_release'
required: true
type: boolean
env:
DOTNET_VERSION: '8.0.x'
jobs:
build-and-package:
name: Build and Package
runs-on: windows-2022
env:
Solution_Name: RailGo.sln
SigningKey_Path: SigningKey.pfx
APPLICATION_ID: 9NTRZ0DMCKGZ
steps:
- name: Checkout
uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Install .NET Core
uses: actions/setup-dotnet@v3
with:
dotnet-version: ${{ env.DOTNET_VERSION }}
- name: Setup MSBuild.exe
uses: microsoft/setup-msbuild@v1
- name: Install StoreBroker
shell: pwsh
run: |
Write-Output "Installing StoreBroker module..."
Install-Module -Name StoreBroker -Force -AllowClobber -Scope CurrentUser
Import-Module StoreBroker
Write-Output "StoreBroker installed successfully"
- name: Restore the application
run: msbuild $env:Solution_Name /t:Restore /p:Configuration=$env:Configuration
env:
Configuration: Release
- name: Update Manifest Version
shell: pwsh
run: |
$AppxManifestPath = "RailGo/Package.appxmanifest"
$newVersion = "${{ github.event.inputs.version }}"
Write-Output "Setting new version: $newVersion"
[xml]$xmlMSIX = Get-Content $AppxManifestPath
$identityNode = $xmlMSIX.Package.Identity
$identityNode.Version = $newVersion
$xmlMSIX.Save($AppxManifestPath)
- name: Create the MSIX Package
run: msbuild $env:Solution_Name /p:AppxBundlePlatforms="$env:Appx_Bundle_Platforms" /p:Configuration=$env:Configuration /p:UapAppxPackageBuildMode=$env:Appx_Package_Build_Mode /p:AppxBundle=$env:Appx_Bundle /p:AppxPackageDir="$env:Appx_Package_Dir" /p:GenerateAppxPackageOnBuild=true /p:AppxPackageSigningEnabled=false
env:
Appx_Bundle: Always
Appx_Bundle_Platforms: x86|x64|ARM64
Appx_Package_Build_Mode: SideloadOnly
Appx_Package_Dir: AppxPackages\
Configuration: Release
- name: Sign .msix
shell: pwsh
run: |
# Get signtool.exe
$signtool = Get-ChildItem -Path "C:\Program Files (x86)\Windows Kits\10\bin\*\x64\signtool.exe" | Select-Object -First 1
# Get File
$msixFile = Get-ChildItem -Path "RailGo/AppxPackages" -Recurse -Filter "*.msix" | Sort-Object LastWriteTime -Descending | Select-Object -First 1
if (-not $msixFile) {
throw "No MSIX file found"
}
Write-Output "Signing file: $($msixFile.FullName)"
# Sign .msix
$pfx_cert_byte = [System.Convert]::FromBase64String("${{ secrets.SIGNING_CERT }}")
[IO.File]::WriteAllBytes("${{ env.SigningKey_Path }}", $pfx_cert_byte)
& $signtool sign /f "${{ env.SigningKey_Path }}" /fd SHA256 /td SHA256 "$($msixFile.FullName)"
Remove-Item "${{ env.SigningKey_Path }}"
Write-Output "File signed successfully"
- name: Upload to Microsoft Store using StoreBroker (Manual Package Cleanup)
shell: pwsh
run: |
$ErrorActionPreference = "Stop"
# Import StoreBroker module
Import-Module StoreBroker
# Find signed MSIX file
Write-Output "Looking for signed MSIX file..."
$msixFile = Get-ChildItem -Path "RailGo/AppxPackages" -Recurse -Filter "*.msix" | Sort-Object LastWriteTime -Descending | Select-Object -First 1
if (-not $msixFile) {
throw "No signed MSIX file found"
}
Write-Output "Found MSIX file: $($msixFile.FullName)"
Write-Output "File size: $([math]::Round($msixFile.Length / 1MB, 2)) MB"
# Setup authentication
Write-Output "Setting up StoreBroker authentication..."
$clientId = "${{ secrets.AZURE_CLIENT_ID }}"
$clientSecret = "${{ secrets.AZURE_CLIENT_SECRET }}"
$tenantId = "${{ secrets.AZURE_TENANT_ID }}"
# Create credentials
$securePassword = ConvertTo-SecureString $clientSecret -AsPlainText -Force
$credential = New-Object System.Management.Automation.PSCredential($clientId, $securePassword)
# Set StoreBroker authentication
Set-StoreBrokerAuthentication -TenantId $tenantId -Credential $credential
# Create output directory
$outPath = "SubmissionPackage"
if (Test-Path $outPath) {
Remove-Item $outPath -Recurse -Force
}
New-Item -ItemType Directory -Path $outPath -Force
# Create basic submission configuration
Write-Output "Creating submission configuration..."
$configContent = @"
{
"targetPublishMode": "Manual",
"targetPublishDate": "",
"notesForReviewer": "Automated submission via GitHub Actions - Version ${{ github.event.inputs.version }}",
"applicationCategory": "Productivity",
"pricing": {
"trialPeriod": "NoFreeTrial",
"marketSpecificPricings": {},
"sales": []
},
"visibility": "Public",
"allowTargetFutureDeviceFamilies": {
"Desktop": true,
"Mobile": false,
"Holographic": false,
"Xbox": false
},
"allowMicrosoftDecideAppAvailabilityToFutureDeviceFamilies": true,
"languages": ["en-US"]
}
"@
$configContent | Out-File -FilePath "$outPath\submission.json" -Encoding UTF8
# Use StoreBroker to create submission package
Write-Output "Creating submission package..."
try {
New-SubmissionPackage -ConfigPath "$outPath\submission.json" -AppxPath $msixFile.FullName -OutPath $outPath -OutName "package"
Write-Output "Submission package created successfully"
} catch {
Write-Error "Failed to create submission package: $($_.Exception.Message)"
exit 1
}
# Manual method: delete old packages first, then add new package
Write-Output "Starting manual package cleanup and update process..."
# 1. Create new submission (clone current published submission)
Write-Output "Step 1: Creating new submission..."
$submission = New-ApplicationSubmission -AppId "${{ env.APPLICATION_ID }}" -Force
Write-Output "New submission created with ID: $($submission.id)"
# 2. Show all current packages in submission
Write-Output "Step 2: Current packages in submission:"
if ($submission.applicationPackages -and $submission.applicationPackages.Count -gt 0) {
for ($i = 0; $i -lt $submission.applicationPackages.Count; $i++) {
$package = $submission.applicationPackages[$i]
Write-Output " Package [$i]: $($package.fileName) - Status: $($package.fileStatus)"
}
# 3. Mark all old packages for deletion
Write-Output "Step 3: Marking all existing packages for deletion..."
foreach ($package in $submission.applicationPackages) {
$package.fileStatus = 'PendingDelete'
Write-Output " Marked for deletion: $($package.fileName)"
}
} else {
Write-Output " No existing packages found in submission"
}
# 4. Parse new package info and add to submission
Write-Output "Step 4: Adding new package to submission..."
$packageJsonPath = Join-Path $outPath "package.json"
if (Test-Path $packageJsonPath) {
$packageJson = Get-Content $packageJsonPath -Encoding UTF8 | ConvertFrom-Json
# Add new package
if ($packageJson.applicationPackages -and $packageJson.applicationPackages.Count -gt 0) {
foreach ($newPackage in $packageJson.applicationPackages) {
$submission.applicationPackages += $newPackage
Write-Output " Added new package: $($newPackage.fileName)"
}
} else {
Write-Error "No application packages found in package.json"
exit 1
}
} else {
Write-Error "package.json not found at: $packageJsonPath"
exit 1
}
# 5. Update submission
Write-Output "Step 5: Updating submission with changes..."
Set-ApplicationSubmission -AppId "${{ env.APPLICATION_ID }}" -UpdatedSubmission $submission
Write-Output "Submission updated successfully"
# 6. Upload package file
Write-Output "Step 6: Uploading package file..."
$packageZipPath = Join-Path $outPath "package.zip"
if (Test-Path $packageZipPath) {
Set-SubmissionPackage -PackagePath $packageZipPath -UploadUrl $submission.fileUploadUrl
Write-Output "Package file uploaded successfully"
} else {
Write-Error "Package zip file not found at: $packageZipPath"
exit 1
}
# 7. Commit submission
Write-Output "Step 7: Committing submission..."
Complete-ApplicationSubmission -AppId "${{ env.APPLICATION_ID }}" -SubmissionId $submission.id
Write-Output "Submission committed successfully"
Write-Output ""
Write-Output "Successfully uploaded to Microsoft Store!"
Write-Output "Submission ID: $($submission.id)"
Write-Output "File: $($msixFile.Name)"
Write-Output "All previous packages have been marked for deletion"
Write-Output "New package has been added"
Write-Output "Visit Partner Center to complete: https://partner.microsoft.com/dashboard"
env:
AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }}
AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }}
AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }}