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