diff --git a/.gitea/workflows/release.yml b/.gitea/workflows/release.yml index b399353..cf6652a 100644 --- a/.gitea/workflows/release.yml +++ b/.gitea/workflows/release.yml @@ -2,8 +2,8 @@ name: Release # Fires only on a merge into main (a push to the main branch). No per-commit # or per-PR CI runs on other branches — this is the single pipeline that turns -# what lands on main into a release, cross-platform binaries, an MSI, and a -# container image. +# what lands on main into a release, cross-platform binaries, a Windows MSI, and +# a container image. # # Docs-only merges are skipped: touching just README/docs/LICENSE does not # produce a new build. @@ -21,9 +21,9 @@ on: jobs: release: - # Label must match a registered Linux runner that has Docker. Pure-Go builds - # (modernc SQLite, CGO off) mean every OS/arch cross-compiles here, and WiX v5 - # builds the Windows MSI on Linux, so one runner produces every artifact. + # Linux runner with Docker. Pure-Go builds (modernc SQLite, CGO off) mean + # every OS/arch cross-compiles here; the Windows MSI is built in the separate + # `msi` job on a Windows runner (WiX only supports Windows). runs-on: ubuntu-host # Grant the auto-injected Actions token the scopes this job needs: push to @@ -32,6 +32,11 @@ jobs: contents: write packages: write + # Consumed by the `msi` job so the installer version and binary match. + outputs: + version: ${{ steps.ver.outputs.version }} + msi_version: ${{ steps.ver.outputs.msi_version }} + env: # Use exactly the toolchain provided by setup-go; never auto-download a # different Go toolchain (a `go install ...@latest` otherwise pulled a @@ -126,7 +131,6 @@ jobs: echo "resolved GOROOT: $GR" echo "GOROOT=$GR" >> "$GITHUB_ENV" GOROOT="$GR" go version - GOROOT="$GR" go tool compile -V || true GOROOT="$GR" go clean -cache - name: Setup Node @@ -134,11 +138,6 @@ jobs: with: node-version: '22' - - name: Setup .NET - uses: actions/setup-dotnet@v4 - with: - dotnet-version: '8.0.x' - - name: Ensure tooling (jq, zip) shell: bash run: | @@ -199,7 +198,7 @@ jobs: # Cross-compile all six targets (CGO off) and package them: .zip for # Windows, .tar.gz elsewhere, each with a .sha256. Stage the windows/amd64 - # binary for the MSI. + # binary separately so the msi job can wrap it. - name: Build binaries working-directory: backend env: @@ -242,24 +241,14 @@ jobs: build linux arm64 linux-arm64 "" ls -la "$DIST" - # Build the Windows MSI with WiX v5 (cross-platform, no OSMF fee). Installs - # to Program Files\OrchestrAD and registers+starts the service via the - # binary's idempotent `initialize`/`remove` commands. - - name: Build MSI - env: - MSI_VERSION: ${{ steps.ver.outputs.msi_version }} - VERSION: ${{ steps.ver.outputs.version }} - run: | - set -euo pipefail - export PATH="$PATH:$HOME/.dotnet/tools" - command -v wix >/dev/null 2>&1 || dotnet tool install --global wix --version 5.0.2 - wix build -arch x64 installer/OrchestrAD.wxs \ - -d Version="$MSI_VERSION" \ - -d BinDir="$GITHUB_WORKSPACE/msistage" \ - -d IconPath="$GITHUB_WORKSPACE/resources/icons/orchestrad.ico" \ - -o "dist/OrchestrAD-${VERSION}-x64.msi" - ( cd dist && sha256sum "OrchestrAD-${VERSION}-x64.msi" > "OrchestrAD-${VERSION}-x64.msi.sha256" ) - ls -la dist + # Hand the windows/amd64 binary to the msi job (which runs on Windows, + # where WiX is supported). + - name: Upload Windows binary for MSI + uses: actions/upload-artifact@v3 + with: + name: msi-bin + path: msistage/orchestrad.exe + retention-days: 1 - name: Resolve registry target id: reg @@ -314,7 +303,8 @@ jobs: docker push "${IMAGE}:latest" echo "Published ${IMAGE}:${VERSION} and ${IMAGE}:latest" - # Create the Gitea release and attach every artifact in dist/. + # Create the Gitea release and attach every binary artifact in dist/. The + # MSI is added later by the msi job. - name: Create Gitea release shell: bash env: @@ -328,8 +318,6 @@ jobs: run: | set -euo pipefail - # Skip creation if a release for this tag already exists (re-run), but - # still (re)upload any assets that are missing below. code="$(curl -sS -o /tmp/rel.json -w '%{http_code}' \ -H "Authorization: token ${TOKEN}" \ "${API_URL}/repos/${REPO}/releases/tags/${VERSION}")" @@ -359,4 +347,63 @@ jobs: -F "attachment=@${asset}" \ "${API_URL}/repos/${REPO}/releases/${rel_id}/assets?name=${name}" >/dev/null done - echo "Release ${VERSION} published with $(ls dist | wc -l) assets." + echo "Release ${VERSION} published with $(ls dist | wc -l) binary assets." + + msi: + # WiX only supports Windows, so the installer is built here on the Windows + # runner using the windows/amd64 binary produced by the release job, then + # attached to the release the previous job created. + needs: release + runs-on: windows-host + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup .NET + uses: actions/setup-dotnet@v4 + with: + dotnet-version: '8.0.x' + + - name: Download Windows binary + uses: actions/download-artifact@v3 + with: + name: msi-bin + path: msistage + + - name: Build and attach MSI + shell: pwsh + env: + API_URL: ${{ github.api_url }} + REPO: ${{ github.repository }} + TOKEN: ${{ github.token }} + VERSION: ${{ needs.release.outputs.version }} + MSI_VERSION: ${{ needs.release.outputs.msi_version }} + run: | + $ErrorActionPreference = 'Stop' + $env:PATH += ";$env:USERPROFILE\.dotnet\tools" + if (-not (Get-Command wix -ErrorAction SilentlyContinue)) { + dotnet tool install --global wix --version 5.0.2 + } + New-Item -ItemType Directory -Force dist | Out-Null + $msi = "dist/OrchestrAD-$($env:VERSION)-x64.msi" + wix build -arch x64 installer/OrchestrAD.wxs ` + -d Version="$($env:MSI_VERSION)" ` + -d BinDir="$PWD/msistage" ` + -d IconPath="$PWD/resources/icons/orchestrad.ico" ` + -o $msi + if ($LASTEXITCODE -ne 0) { throw "wix build failed ($LASTEXITCODE)" } + + $hash = (Get-FileHash $msi -Algorithm SHA256).Hash.ToLower() + "$hash OrchestrAD-$($env:VERSION)-x64.msi" | Set-Content "$msi.sha256" + + $headers = @{ Authorization = "token $($env:TOKEN)" } + $rel = Invoke-RestMethod -Headers $headers -Uri "$($env:API_URL)/repos/$($env:REPO)/releases/tags/$($env:VERSION)" + foreach ($asset in @($msi, "$msi.sha256")) { + $name = Split-Path $asset -Leaf + Write-Host "Uploading $name" + curl.exe -sS -X POST -H "Authorization: token $($env:TOKEN)" ` + -F "attachment=@$asset" ` + "$($env:API_URL)/repos/$($env:REPO)/releases/$($rel.id)/assets?name=$name" | Out-Null + } + Write-Host "MSI attached to release $($env:VERSION)."