214 lines
8.8 KiB
YAML
214 lines
8.8 KiB
YAML
name: Manual Beta Release
|
|
|
|
on:
|
|
workflow_dispatch:
|
|
inputs:
|
|
release_title:
|
|
description: Optional GitHub release title override
|
|
required: false
|
|
type: string
|
|
|
|
permissions:
|
|
contents: write
|
|
packages: read
|
|
|
|
jobs:
|
|
release-beta:
|
|
name: Build and publish beta release
|
|
runs-on: windows-latest
|
|
env:
|
|
PROJECT_PATH: Wino.Mail.WinUI/Wino.Mail.WinUI.csproj
|
|
MANIFEST_PATH: Wino.Mail.WinUI/Package.appxmanifest
|
|
CHANGELOG_PATH: CHANGELOG.md
|
|
NUGET_CONFIG_PATH: ${{ github.workspace }}\nuget.config
|
|
PACKAGE_OUTPUT_DIR: ${{ github.workspace }}\artifacts\package
|
|
RELEASE_OUTPUT_DIR: ${{ github.workspace }}\artifacts\release
|
|
CERTIFICATE_PFX_PATH: ${{ github.workspace }}\artifacts\signing\beta-signing-cert.pfx
|
|
CERTIFICATE_CER_PATH: ${{ github.workspace }}\artifacts\release\Wino-Mail-Beta.cer
|
|
steps:
|
|
- name: Checkout selected branch
|
|
uses: actions/checkout@v4
|
|
with:
|
|
ref: ${{ github.ref }}
|
|
fetch-depth: 0
|
|
|
|
- name: Fetch tags from origin
|
|
shell: pwsh
|
|
run: git fetch origin --force --tags
|
|
|
|
- name: Validate release secrets
|
|
shell: pwsh
|
|
env:
|
|
BETA_SIGNING_CERT_PFX_BASE64: ${{ secrets.BETA_SIGNING_CERT_PFX_BASE64 }}
|
|
run: |
|
|
if ([string]::IsNullOrWhiteSpace($env:BETA_SIGNING_CERT_PFX_BASE64)) {
|
|
throw "Missing required secret: BETA_SIGNING_CERT_PFX_BASE64"
|
|
}
|
|
|
|
- name: Setup .NET SDK
|
|
uses: actions/setup-dotnet@v4
|
|
with:
|
|
dotnet-version: 10.0.x
|
|
env:
|
|
NUGET_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
|
|
- name: Compute beta version and release metadata
|
|
id: metadata
|
|
shell: pwsh
|
|
env:
|
|
RELEASE_TITLE_INPUT: ${{ github.event.inputs.release_title }}
|
|
run: |
|
|
$manifestPath = Join-Path $env:GITHUB_WORKSPACE $env:MANIFEST_PATH
|
|
if (-not (Test-Path $manifestPath)) {
|
|
throw "Package manifest not found: $manifestPath"
|
|
}
|
|
|
|
$changelogPath = Join-Path $env:GITHUB_WORKSPACE $env:CHANGELOG_PATH
|
|
if (-not (Test-Path $changelogPath)) {
|
|
throw "Release notes file not found: $changelogPath"
|
|
}
|
|
|
|
[xml]$manifest = Get-Content -LiteralPath $manifestPath
|
|
$identityNode = $manifest.Package.Identity
|
|
if (-not $identityNode) {
|
|
throw "Could not locate the Package/Identity node in $manifestPath"
|
|
}
|
|
|
|
$currentVersionText = [string]$identityNode.Version
|
|
if ($currentVersionText -notmatch '^(?<major>\d+)\.(?<minor>\d+)\.(?<patch>\d+)\.(?<revision>\d+)$') {
|
|
throw "Manifest version '$currentVersionText' is not a four-part numeric version."
|
|
}
|
|
|
|
$baseVersion = '{0}.{1}.{2}' -f $Matches.major, $Matches.minor, $Matches.patch
|
|
$packageVersion = $currentVersionText
|
|
|
|
$matchingTags = git tag --list "v$baseVersion.*"
|
|
$releaseNumbers = @()
|
|
foreach ($tag in $matchingTags) {
|
|
if ($tag -match "^v$([regex]::Escape($baseVersion))\.(\d+)$") {
|
|
$releaseNumbers += [int]$Matches[1]
|
|
}
|
|
}
|
|
|
|
$nextReleaseNumber = if ($releaseNumbers.Count -gt 0) { ($releaseNumbers | Measure-Object -Maximum).Maximum + 1 } else { 1 }
|
|
$releaseTag = "v$baseVersion.$nextReleaseNumber"
|
|
$releaseTitleInput = $env:RELEASE_TITLE_INPUT
|
|
$releaseTitle = if ([string]::IsNullOrWhiteSpace($releaseTitleInput)) { $releaseTag } else { $releaseTitleInput.Trim() }
|
|
|
|
$headSha = (git rev-parse HEAD).Trim()
|
|
if ([string]::IsNullOrWhiteSpace($headSha)) {
|
|
throw "Failed to resolve the checked out commit SHA."
|
|
}
|
|
|
|
$notesInput = Get-Content -LiteralPath $changelogPath -Raw
|
|
if ([string]::IsNullOrWhiteSpace($notesInput)) {
|
|
throw "Release notes file is empty: $changelogPath"
|
|
}
|
|
|
|
$notesInput = $notesInput.Trim()
|
|
New-Item -ItemType Directory -Path $env:RELEASE_OUTPUT_DIR -Force | Out-Null
|
|
$releaseNotesPath = Join-Path $env:RELEASE_OUTPUT_DIR 'beta-release-notes.md'
|
|
$notesInput | Set-Content -LiteralPath $releaseNotesPath -Encoding utf8
|
|
|
|
"package_version=$packageVersion" >> $env:GITHUB_OUTPUT
|
|
"base_version=$baseVersion" >> $env:GITHUB_OUTPUT
|
|
"release_tag=$releaseTag" >> $env:GITHUB_OUTPUT
|
|
"release_title=$releaseTitle" >> $env:GITHUB_OUTPUT
|
|
"release_notes_path=$releaseNotesPath" >> $env:GITHUB_OUTPUT
|
|
"head_sha=$headSha" >> $env:GITHUB_OUTPUT
|
|
|
|
- name: Materialize signing certificate
|
|
shell: pwsh
|
|
env:
|
|
BETA_SIGNING_CERT_PFX_BASE64: ${{ secrets.BETA_SIGNING_CERT_PFX_BASE64 }}
|
|
run: |
|
|
$signingDir = Split-Path -Parent $env:CERTIFICATE_PFX_PATH
|
|
New-Item -ItemType Directory -Path $signingDir -Force | Out-Null
|
|
[IO.File]::WriteAllBytes($env:CERTIFICATE_PFX_PATH, [Convert]::FromBase64String($env:BETA_SIGNING_CERT_PFX_BASE64))
|
|
|
|
$certificate = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2($env:CERTIFICATE_PFX_PATH, $null, [System.Security.Cryptography.X509Certificates.X509KeyStorageFlags]::Exportable)
|
|
|
|
New-Item -ItemType Directory -Path (Split-Path -Parent $env:CERTIFICATE_CER_PATH) -Force | Out-Null
|
|
[IO.File]::WriteAllBytes($env:CERTIFICATE_CER_PATH, $certificate.Export([System.Security.Cryptography.X509Certificates.X509ContentType]::Cert))
|
|
|
|
- name: Restore WinUI project dependencies
|
|
shell: pwsh
|
|
run: |
|
|
if (-not (Test-Path $env:NUGET_CONFIG_PATH)) {
|
|
throw "NuGet config file not found: $env:NUGET_CONFIG_PATH"
|
|
}
|
|
|
|
dotnet restore $env:PROJECT_PATH `
|
|
--configfile $env:NUGET_CONFIG_PATH `
|
|
-p:Platform=x64 `
|
|
/p:RestoreConfigFile="$env:NUGET_CONFIG_PATH"
|
|
|
|
- name: Build MSIX bundle
|
|
shell: pwsh
|
|
run: |
|
|
New-Item -ItemType Directory -Path $env:PACKAGE_OUTPUT_DIR -Force | Out-Null
|
|
|
|
dotnet build $env:PROJECT_PATH `
|
|
--configuration Release `
|
|
--no-restore `
|
|
--configfile $env:NUGET_CONFIG_PATH `
|
|
/p:Platform=x64 `
|
|
/p:RestoreConfigFile="$env:NUGET_CONFIG_PATH" `
|
|
/p:GenerateAppxPackageOnBuild=true `
|
|
/p:UapAppxPackageBuildMode=SideloadOnly `
|
|
/p:AppxBundle=Always `
|
|
/p:AppxBundlePlatforms="x86|x64|arm64" `
|
|
/p:AppxPackageDir="$env:PACKAGE_OUTPUT_DIR\\" `
|
|
/p:AppxPackageVersion=${{ steps.metadata.outputs.package_version }} `
|
|
/p:PackageCertificateKeyFile="$env:CERTIFICATE_PFX_PATH" `
|
|
/p:PackageCertificatePassword= `
|
|
/p:PackageCertificateThumbprint= `
|
|
/p:AppxPackageSigningEnabled=true
|
|
|
|
- name: Collect packaged artifacts
|
|
id: package
|
|
shell: pwsh
|
|
run: |
|
|
$bundle = Get-ChildItem -Path $env:PACKAGE_OUTPUT_DIR -Recurse -Filter *.msixbundle | Select-Object -First 1
|
|
if (-not $bundle) {
|
|
throw "No .msixbundle file was generated under $env:PACKAGE_OUTPUT_DIR"
|
|
}
|
|
|
|
$packageZipPath = Join-Path $env:RELEASE_OUTPUT_DIR 'Wino-Mail-Beta-PackageOutput.zip'
|
|
if (Test-Path $packageZipPath) {
|
|
Remove-Item -LiteralPath $packageZipPath -Force
|
|
}
|
|
|
|
Compress-Archive -Path (Join-Path $env:PACKAGE_OUTPUT_DIR '*') -DestinationPath $packageZipPath -Force
|
|
|
|
"bundle_path=$($bundle.FullName)" >> $env:GITHUB_OUTPUT
|
|
"bundle_name=$($bundle.Name)" >> $env:GITHUB_OUTPUT
|
|
"package_zip_path=$packageZipPath" >> $env:GITHUB_OUTPUT
|
|
|
|
- name: Upload workflow artifacts
|
|
uses: actions/upload-artifact@v4
|
|
with:
|
|
name: beta-release-assets-${{ steps.metadata.outputs.release_tag }}
|
|
path: |
|
|
${{ steps.package.outputs.bundle_path }}
|
|
${{ env.CERTIFICATE_CER_PATH }}
|
|
${{ steps.metadata.outputs.release_notes_path }}
|
|
${{ steps.package.outputs.package_zip_path }}
|
|
if-no-files-found: error
|
|
|
|
- name: Create GitHub prerelease
|
|
shell: pwsh
|
|
env:
|
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
run: |
|
|
gh release create "${{ steps.metadata.outputs.release_tag }}" `
|
|
"${{ steps.package.outputs.bundle_path }}" `
|
|
"${{ env.CERTIFICATE_CER_PATH }}" `
|
|
"${{ steps.metadata.outputs.release_notes_path }}" `
|
|
"${{ steps.package.outputs.package_zip_path }}" `
|
|
--repo "${{ github.repository }}" `
|
|
--target "${{ steps.metadata.outputs.head_sha }}" `
|
|
--title "${{ steps.metadata.outputs.release_title }}" `
|
|
--notes-file "${{ steps.metadata.outputs.release_notes_path }}" `
|
|
--prerelease
|