# Gitea Actions CI gate for the self-hosted runners. # # Deliberately does NOT use actions/setup-dotnet: data.forgejo.org (the mirror # Gitea resolves actions from) does not host that action at all, and the # self-hosted runners carry the pinned SDK band from global.json already. # actions/checkout IS mirrored, so it is used normally. # # The suite runs through tools/run-release-gate.ps1 rather than a bare # `dotnet test`: that script owns the xUnit trait-lane filter which excludes # the InstalledDat / Live / Manual / OS-specific lanes. A bare `dotnet test` # fails ~36 tests by design because those lanes assert their own preconditions. name: CI on: push: branches: [main] workflow_dispatch: jobs: windows-gate: runs-on: windows-latest timeout-minutes: 45 steps: - uses: actions/checkout@v6 - name: Verify the pinned SDK band resolves shell: pwsh run: | dotnet --version dotnet --list-sdks # NOT tools/run-release-gate.ps1 here. That script redirects every child # process to its own log file, so the step emits nothing for minutes at a # time; Forgejo treats a task that stops reporting as a zombie and fails # it while the work is still running (observed: job marked failed with 20 # dotnet processes still alive and a complete 8.7 MB TRX on disk). Running # the projects directly keeps output streaming. The script stays the # canonical LOCAL gate; the trait filter below is copied from its default. - name: Build shell: pwsh run: dotnet build AcDream.slnx -c Release --nologo - name: Test (lane-filtered, streaming) shell: pwsh run: | $ErrorActionPreference = 'Stop' $filter = 'Lane!=InstalledDat&Lane!=PreparedPackage&Lane!=Live&Lane!=Manual&Lane!=Windows&Lane!=Linux&Lane!=SystemFont&Purpose!=Diagnostic&Status!=KnownFailure' $failed = @() foreach ($proj in Get-ChildItem tests -Directory | Sort-Object Name) { $csproj = Join-Path $proj.FullName "$($proj.Name).csproj" if (-not (Test-Path $csproj)) { continue } Write-Host "::group::$($proj.Name)" dotnet test $csproj -c Release --no-build --nologo --filter $filter if ($LASTEXITCODE -ne 0) { $failed += $proj.Name } Write-Host "::endgroup::" } if ($failed.Count) { throw "Failed test projects: $($failed -join ', ')" } linux-portable: runs-on: ubuntu-latest timeout-minutes: 45 steps: - uses: actions/checkout@v6 - name: Portable closure (Linux lanes run here, not on Windows) run: | set -e dotnet --version for p in \ tests/AcDream.Platform.Tests \ tests/AcDream.Core.Tests \ tests/AcDream.Core.Net.Tests \ tests/AcDream.Content.Tests \ tests/AcDream.Runtime.Tests \ tests/AcDream.Headless.Tests \ tests/AcDream.Launcher.Core.Tests \ tests/AcDream.UI.Abstractions.Tests ; do echo "::group::$p" # MaxParallelThreads is capped because this runner is a small # container. Under full-assembly parallel load # FakeAceTransportTests.PausedSelector_SeededDroppedServerReady_ # RecoversOnIdleSweep took 37 s and missed its 2 s real-time wait, # while passing 5/5 in ~350 ms in isolation. Timing-sensitive, not # broken: starve it less rather than edit Campaign N transport code. dotnet test "$p" -c Release --nologo \ --filter 'Lane!=InstalledDat&Lane!=PreparedPackage&Lane!=Live&Lane!=Manual&Lane!=Windows&Lane!=SystemFont&Purpose!=Diagnostic&Status!=KnownFailure' \ -- xUnit.MaxParallelThreads=2 echo "::endgroup::" done release: # Same workflow rather than a workflow_run trigger: workflow_run is a # GitHub feature whose Forgejo support is unreliable, while `needs` is # guaranteed. A red gate therefore cannot publish. needs: [windows-gate, linux-portable] runs-on: windows-latest timeout-minutes: 60 steps: - uses: actions/checkout@v6 - name: Compute release version id: ver shell: pwsh run: | $v = '0.1.0-build.{0}' -f ([DateTime]::UtcNow.ToString('yyyyMMddHHmm')) "version=$v" | Out-File -FilePath $env:GITHUB_OUTPUT -Append -Encoding utf8 Write-Host "release version: $v" - name: Build payloads with release-attachment URLs shell: pwsh env: TAG: ${{ steps.ver.outputs.version }} run: | ./tools/publish-bin.ps1 -Version $env:TAG -BaseUrl "${{ github.server_url }}/${{ github.repository }}/releases/download/$env:TAG" - name: Create the release and upload payloads shell: pwsh env: TAG: ${{ steps.ver.outputs.version }} TOKEN: ${{ secrets.GITEA_TOKEN }} run: | $ErrorActionPreference = 'Stop' $api = "${{ github.server_url }}/api/v1/repos/${{ github.repository }}" $headers = @{ Authorization = "token $env:TOKEN" } $body = @{ tag_name = $env:TAG name = "acdream alpha $env:TAG" body = "Automated alpha build from ${{ github.sha }}." draft = $false prerelease = $true target_commitish = 'main' } | ConvertTo-Json $release = Invoke-RestMethod -Method Post -Uri "$api/releases" -Headers $headers -ContentType 'application/json' -Body $body Write-Host "created release id=$($release.id)" foreach ($f in Get-ChildItem bin -File) { Write-Host ("uploading {0} ({1:N1} MB)" -f $f.Name, ($f.Length/1MB)) Invoke-RestMethod -Method Post -Headers $headers -Uri "$api/releases/$($release.id)/assets?name=$($f.Name)" -Form @{ attachment = Get-Item $f.FullName } | Out-Null } - name: Point the launcher manifest at the new release shell: pwsh env: TAG: ${{ steps.ver.outputs.version }} TOKEN: ${{ secrets.GITEA_TOKEN }} run: | $ErrorActionPreference = 'Stop' # dist carries ONLY manifest.json (~500 bytes), force-replaced each # publish, never pushed to GitHub. Payloads stay in release # attachments, outside git history entirely. $url = "${{ github.server_url }}/${{ github.repository }}.git" -replace '^https://', "https://x:$env:TOKEN@" git config --global user.email 'ci@acdream.local' git config --global user.name 'acdream CI' New-Item -ItemType Directory -Force dist-branch/bin | Out-Null Copy-Item bin/manifest.json dist-branch/bin/manifest.json Push-Location dist-branch git init -q git checkout -q -b dist git add -f bin/manifest.json git commit -q -m "release: manifest for $env:TAG" git push -q --force $url dist:dist Pop-Location Write-Host "manifest published for $env:TAG"