# Build the punktfunk Windows client as signed MSIX packages (x64 + ARM64) and publish them to # Gitea's generic package registry, so Windows boxes can download + install a real package (Start # tile, clean install/uninstall) instead of a loose exe. Runs on the self-hosted Windows runner # (host mode; scripts/ci/setup-windows-runner.ps1) — the MSVC/WinUI/FFmpeg toolchain + the Windows # SDK's makeappx/signtool are baked into the runner's daemon env, same as windows.yml. # # Both arches come off the ONE x64 runner: x86_64 natively, aarch64 cross-compiled (the x64 MSVC # toolset has the ARM64 cross compiler; the matrix points FFMPEG_DIR at the ARM64 FFmpeg tree). See # windows.yml for the cross-build rationale + the BOM/MAX_PATH runner gotchas. # # Registry (public, unom org): https://git.unom.io/unom/-/packages (generic group) # Packaging internals: clients/windows/packaging/README.md. # # Versioning — single project version; MSIX requires a strictly 4-part numeric version, so: # vX.Y.Z tag -> X.Y.Z.0 (THE release; any -rc/+meta pre-release suffix is dropped for MSIX). # Published to the generic registry + the stable `latest/` alias + attached to the # unified Gitea Release alongside every other platform's artifact. # main push / dispatch -> 0.3..0 (canary; climbs monotonically by run number). # Published to the generic registry + the `canary/` alias. # Both arches share the version; artifacts are arch-suffixed (..._x64.msix / ..._arm64.msix). # # Signing (packaging/pack-msix.ps1): if the MSIX_CERT_PFX_B64 / MSIX_CERT_PASSWORD Actions secrets # are set (a real or shared code-signing .pfx whose subject DN == Publisher), the package is signed # with them. Otherwise an ephemeral self-signed cert is generated and its public .cer is published # next to the .msix (users import it to Trusted People before install). Drop in a real cert later # with no workflow change — just add the secrets (+ pass -Publisher if its subject differs). name: windows-msix on: push: branches: [main] paths: - 'clients/windows/**' - 'crates/punktfunk-core/**' - 'Cargo.lock' - 'Cargo.toml' - '.gitea/workflows/windows-msix.yml' tags: ['v*'] workflow_dispatch: env: REGISTRY: git.unom.io OWNER: unom PKG: punktfunk-client-windows jobs: package: runs-on: windows-amd64 timeout-minutes: 90 strategy: fail-fast: false matrix: include: - arch: x64 target: x86_64-pc-windows-msvc ffmpeg: C:\Users\Public\ffmpeg td: C:\t - arch: arm64 target: aarch64-pc-windows-msvc ffmpeg: C:\Users\Public\ffmpeg-arm64 td: C:\t-a64 steps: - uses: actions/checkout@v4 - name: Configure + version shell: pwsh run: | # windows-reactor's build.rs unwraps CARGO_WORKSPACE_DIR; CARGO_TARGET_DIR (per-arch, short) # dodges the MAX_PATH wall in the CMake-from-source crates (see windows.yml). FFMPEG_DIR # selects the arch's import libs + is read by pack-msix.ps1 for the runtime DLLs. All via # GITHUB_ENV. "CARGO_WORKSPACE_DIR=$env:GITHUB_WORKSPACE" | Out-File -FilePath $env:GITHUB_ENV -Append -Encoding utf8 "CARGO_TARGET_DIR=${{ matrix.td }}" | Out-File -FilePath $env:GITHUB_ENV -Append -Encoding utf8 "FFMPEG_DIR=${{ matrix.ffmpeg }}" | Out-File -FilePath $env:GITHUB_ENV -Append -Encoding utf8 rustup target add ${{ matrix.target }} $parts = if ($env:GITHUB_REF -like 'refs/tags/v*') { # MSIX needs a purely-numeric 4-part version: drop any -rc/+meta pre-release suffix. (($env:GITHUB_REF_NAME -replace '^v', '') -replace '[-+].*$', '').Split('.') } else { @('0', '3', $env:GITHUB_RUN_NUMBER) } while ($parts.Count -lt 4) { $parts += '0' } $v = ($parts[0..3] -join '.') "MSIX_VERSION=$v" | Out-File -FilePath $env:GITHUB_ENV -Append -Encoding utf8 Write-Output "MSIX version $v arch ${{ matrix.arch }} target ${{ matrix.target }}" - name: Build (release) shell: pwsh run: cargo build --release -p punktfunk-client-windows --target ${{ matrix.target }} - name: Pack + sign MSIX shell: pwsh env: MSIX_CERT_PFX_B64: ${{ secrets.MSIX_CERT_PFX_B64 }} MSIX_CERT_PASSWORD: ${{ secrets.MSIX_CERT_PASSWORD }} run: | & clients/windows/packaging/pack-msix.ps1 ` -Version $env:MSIX_VERSION -Arch ${{ matrix.arch }} ` -TargetDir ${{ matrix.td }}\${{ matrix.target }}\release -OutDir ${{ matrix.td }}\msix - name: Publish to Gitea generic registry shell: pwsh env: REGISTRY_TOKEN: ${{ secrets.REGISTRY_TOKEN }} run: | $PSNativeCommandUseErrorActionPreference = $false $base = "https://$($env:REGISTRY)/api/packages/$($env:OWNER)/generic/$($env:PKG)" # stable release -> `latest/` alias; canary main build -> `canary/` alias. $alias = if ($env:GITHUB_REF -like 'refs/tags/v*') { 'latest' } else { 'canary' } # version-less, arch-suffixed alias names so each channel keeps one predictable URL. $aliasNames = @{ "$($env:MSIX_PATH)" = "$($env:PKG)_${{ matrix.arch }}.msix" "$($env:MSIX_CER_PATH)" = "$($env:PKG)_${{ matrix.arch }}.cer" } $files = @($env:MSIX_PATH, $env:MSIX_CER_PATH) | Where-Object { $_ -and (Test-Path $_) } if (-not $files) { throw "pack produced no artifacts to publish" } function Put($f, $url) { curl.exe -fsS --user "enricobuehler:$($env:REGISTRY_TOKEN)" --upload-file "$f" "$url" if ($LASTEXITCODE -ne 0) { throw "upload failed ($LASTEXITCODE): $url" } Write-Output "published $url" } foreach ($f in $files) { $name = Split-Path $f -Leaf # 1) immutable, versioned path Put $f "$base/$($env:MSIX_VERSION)/$name" # 2) channel alias (delete-then-reupload; the generic registry 409s on an existing file) $an = $aliasNames["$f"] curl.exe -fsS -o NUL --user "enricobuehler:$($env:REGISTRY_TOKEN)" -X DELETE "$base/$alias/$an" 2>$null Put $f "$base/$alias/$an" } # On a real release, also attach the MSIX (+ its .cer) to the unified Gitea Release. Both # arch legs attach to the same release concurrently — the helper's create-or-fetch handles # the race, and x64/arm64 filenames differ so the assets don't collide. - name: Attach MSIX to the Gitea release (stable tags only) if: startsWith(gitea.ref, 'refs/tags/v') shell: pwsh env: GITEA_TOKEN: ${{ secrets.REGISTRY_TOKEN }} run: | . scripts/ci/gitea-release.ps1 $rid = Ensure-GiteaRelease -Tag $env:GITHUB_REF_NAME -Name $env:GITHUB_REF_NAME -Prerelease 'auto' foreach ($f in @($env:MSIX_PATH, $env:MSIX_CER_PATH)) { if ($f -and (Test-Path $f)) { Upsert-GiteaAsset -ReleaseId $rid -File $f } }