Files
2026-07-13 12:33:35 +08:00

253 lines
9.7 KiB
YAML

name: Build Release
on:
push:
tags:
- 'v*'
workflow_dispatch:
inputs:
allow_unsigned:
description: Allow unsigned artifacts for a manual test build
required: false
default: false
type: boolean
permissions:
contents: write
id-token: write
env:
ALLOW_UNSIGNED: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.allow_unsigned == 'true' }}
HAS_AZURE_SIGNING: ${{ secrets.AZURE_CLIENT_ID != '' && secrets.AZURE_CLIENT_SECRET != '' && secrets.AZURE_SUBSCRIPTION_ID != '' && secrets.AZURE_TENANT_ID != '' }}
TRUSTED_SIGNING_ENDPOINT: ${{ vars.AZURE_TRUSTED_SIGNING_ENDPOINT || 'https://wus2.codesigning.azure.net/' }}
TRUSTED_SIGNING_ACCOUNT: ${{ vars.AZURE_TRUSTED_SIGNING_ACCOUNT || 'hanselman' }}
TRUSTED_SIGNING_PROFILE: ${{ vars.AZURE_CERTIFICATE_PROFILE_NAME || 'WindowsEdgeLight' }}
jobs:
build:
runs-on: windows-latest
steps:
- name: Checkout code
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
with:
fetch-depth: 0
- name: Setup .NET
uses: actions/setup-dotnet@67a3573c9a986a3f9c594539f4ab511d57bb3ce9 # v4
with:
dotnet-version: '10.0.x'
- name: Require signing configuration
if: env.HAS_AZURE_SIGNING != 'true' && env.ALLOW_UNSIGNED != 'true'
shell: pwsh
run: |
throw "Trusted Signing is not configured. Set the Azure signing secrets/variables, or use workflow_dispatch with allow_unsigned=true for a one-off dry run."
- name: Restore dependencies
run: dotnet restore src/PeekDesktop/PeekDesktop.csproj
- name: Run interop safety harness
run: dotnet run --project src/PeekDesktop.InteropHarness/PeekDesktop.InteropHarness.csproj -- 10000
- name: Set version environment variables
shell: pwsh
env:
REF_TYPE: ${{ github.ref_type }}
REF_NAME: ${{ github.ref_name }}
RUN_NUMBER: ${{ github.run_number }}
run: |
if ($env:REF_TYPE -eq 'tag') {
$version = $env:REF_NAME.TrimStart('v')
} else {
$version = "0.0.0-ci.$env:RUN_NUMBER"
}
$numericVersion = ($version -split '-', 2)[0]
$parts = $numericVersion.Split('.')
if ($parts.Length -lt 2) {
throw "Version '$version' is not in a supported format."
}
$major = [int]$parts[0]
$minor = [int]$parts[1]
$patch = if ($parts.Length -ge 3) { [int]$parts[2] } else { 0 }
$fullVersion = $version
$assemblyVersion = "$major.$minor.$patch.0"
Write-Host "Using semantic version: $version"
Write-Host "Using full version: $fullVersion"
Write-Host "Using assembly version: $assemblyVersion"
echo "VERSION=$version" >> $env:GITHUB_ENV
echo "FULLVERSION=$fullVersion" >> $env:GITHUB_ENV
echo "ASSEMBLY_VERSION=$assemblyVersion" >> $env:GITHUB_ENV
- name: Build x64
run: |
dotnet publish src/PeekDesktop/PeekDesktop.csproj `
-c Release `
-r win-x64 `
/p:PublishSingleFile=true `
/p:DebugType=None `
/p:DebugSymbols=false `
/p:Version=$env:VERSION `
/p:AssemblyVersion=$env:ASSEMBLY_VERSION `
/p:FileVersion=$env:ASSEMBLY_VERSION `
/p:AssemblyInformationalVersion=$env:FULLVERSION `
--self-contained
- name: Build ARM64
run: |
dotnet publish src/PeekDesktop/PeekDesktop.csproj `
-c Release `
-r win-arm64 `
/p:PublishSingleFile=true `
/p:DebugType=None `
/p:DebugSymbols=false `
/p:Version=$env:VERSION `
/p:AssemblyVersion=$env:ASSEMBLY_VERSION `
/p:FileVersion=$env:ASSEMBLY_VERSION `
/p:AssemblyInformationalVersion=$env:FULLVERSION `
--self-contained
- name: Azure Login
if: env.HAS_AZURE_SIGNING == 'true'
uses: azure/login@a457da9ea143d694b1b9c7c869ebb04ebe844ef5 # v2
with:
creds: '{"clientId":"${{ secrets.AZURE_CLIENT_ID }}","clientSecret":"${{ secrets.AZURE_CLIENT_SECRET }}","subscriptionId":"${{ secrets.AZURE_SUBSCRIPTION_ID }}","tenantId":"${{ secrets.AZURE_TENANT_ID }}"}'
- name: Sign executables with Trusted Signing
if: env.HAS_AZURE_SIGNING == 'true'
uses: azure/trusted-signing-action@db7a3a6bd3912025c705162fb7475389f5b69ec6 # v1
with:
azure-tenant-id: ${{ secrets.AZURE_TENANT_ID }}
azure-client-id: ${{ secrets.AZURE_CLIENT_ID }}
azure-client-secret: ${{ secrets.AZURE_CLIENT_SECRET }}
endpoint: ${{ env.TRUSTED_SIGNING_ENDPOINT }}
signing-account-name: ${{ env.TRUSTED_SIGNING_ACCOUNT }}
certificate-profile-name: ${{ env.TRUSTED_SIGNING_PROFILE }}
files: |
${{ github.workspace }}\src\PeekDesktop\bin\Release\net10.0-windows\win-x64\publish\PeekDesktop.exe
${{ github.workspace }}\src\PeekDesktop\bin\Release\net10.0-windows\win-arm64\publish\PeekDesktop.exe
file-digest: SHA256
timestamp-rfc3161: http://timestamp.acs.microsoft.com
timestamp-digest: SHA256
- name: Verify signatures
if: env.HAS_AZURE_SIGNING == 'true'
shell: pwsh
run: |
$files = @(
"src/PeekDesktop/bin/Release/net10.0-windows/win-x64/publish/PeekDesktop.exe",
"src/PeekDesktop/bin/Release/net10.0-windows/win-arm64/publish/PeekDesktop.exe"
)
foreach ($file in $files) {
$signature = Get-AuthenticodeSignature -FilePath $file
Write-Host "$file => $($signature.Status)"
if ($signature.Status -ne 'Valid') {
throw "Signature verification failed for $file"
}
}
- name: Signing skipped
if: env.HAS_AZURE_SIGNING != 'true' && env.ALLOW_UNSIGNED == 'true'
shell: pwsh
run: |
if ('${{ github.ref_type }}' -eq 'tag') {
throw "Unsigned builds are not allowed for tagged releases. Configure Azure Trusted Signing secrets first."
}
Write-Host "Trusted Signing is not configured for this repo yet; continuing only because this is not a tagged release."
- name: Prepare artifacts
shell: pwsh
run: |
New-Item -ItemType Directory -Path artifacts -Force
$version = $env:VERSION
New-Item -ItemType Directory -Path "temp-x64" -Force
Copy-Item "src/PeekDesktop/bin/Release/net10.0-windows/win-x64/publish/PeekDesktop.exe" -Destination "temp-x64/"
Copy-Item "README.md" -Destination "temp-x64/"
Compress-Archive -Path "temp-x64/*" -DestinationPath "artifacts/PeekDesktop-v$version-win-x64.zip"
New-Item -ItemType Directory -Path "temp-arm64" -Force
Copy-Item "src/PeekDesktop/bin/Release/net10.0-windows/win-arm64/publish/PeekDesktop.exe" -Destination "temp-arm64/"
Copy-Item "README.md" -Destination "temp-arm64/"
Compress-Archive -Path "temp-arm64/*" -DestinationPath "artifacts/PeekDesktop-v$version-win-arm64.zip"
Remove-Item "temp-x64" -Recurse -Force
Remove-Item "temp-arm64" -Recurse -Force
- name: Upload artifacts
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with:
name: PeekDesktop-Release
path: artifacts/*.zip
- name: Create immutable-style release
if: startsWith(github.ref, 'refs/tags/')
shell: pwsh
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
$tag = "${{ github.ref_name }}"
$ownerRepo = "${{ github.repository }}"
$headSha = "${{ github.sha }}"
# Reject any attempt to mutate an existing published tag release.
gh release view $tag --repo $ownerRepo *> $null
if ($LASTEXITCODE -eq 0) {
throw "Release '$tag' already exists. Refusing to mutate published releases."
}
# Ensure the pushed tag still points at the exact commit that triggered this run.
$tagSha = (git rev-list -n 1 $tag).Trim()
if ([string]::IsNullOrWhiteSpace($tagSha) -or ($tagSha -ne $headSha)) {
throw "Tag '$tag' points to '$tagSha' but workflow was triggered for '$headSha'. Aborting."
}
$notes = @"
## PeekDesktop $tag
**Click empty desktop wallpaper or empty taskbar space to peek at it — just like macOS Sonoma.**
### 📦 Downloads
| File | Platform |
|------|----------|
| `PeekDesktop-$tag-win-x64.zip` | Intel/AMD 64-bit Windows |
| `PeekDesktop-$tag-win-arm64.zip` | ARM64 Windows (Surface Pro X, etc.) |
**Self-contained single-file** - Extract and run, no .NET installation required.
### 👀 How It Works
- Click empty wallpaper or empty taskbar area -> reveal the desktop
- Use tray options to enable desktop clicks, taskbar clicks, or taskbar-only activation
- Click or drag desktop icons without triggering peek
- Click any window, the taskbar, or the wallpaper again -> everything restores
- Includes Explorer Show Desktop by default plus optional experimental styles
- Check for updates -> opens the latest GitHub Release page
- Winget submission starts automatically after tagged releases; install with `winget install Hanselman.PeekDesktop` after the community manifest is accepted
"@
Set-Content -Path release-notes.md -Value $notes -Encoding UTF8
gh release create $tag `
--repo $ownerRepo `
--verify-tag `
--generate-notes `
--notes-file release-notes.md `
artifacts/*.zip
winget-submit:
needs: build
if: startsWith(github.ref, 'refs/tags/')
permissions:
contents: read
uses: ./.github/workflows/winget-submit.yml
with:
tag: ${{ github.ref_name }}
secrets: inherit