name: Publish Windows Release to Gitea # Builds the Windows release on GitHub-hosted runners and publishes it to the # release of the matching tag in the Gitea repository. It is triggered by tags # mirrored from Gitea by .gitea/workflows/windows-release.yml (or by any tag # pushed to this fork directly). # # This workflow is skipped everywhere except GitHub and except the # yorickr/plezy fork, which owns the GITEA_TOKEN secret. It never creates a # GitHub release; the Gitea release is the artifact channel. on: push: tags: - "*" env: # Only place this workflow names the SDK; .github/actions/setup-flutter-git pins the same release. FLUTTER_VERSION: "3.44.0" # Same defines as build.yml, except Sentry is always enabled: these builds are # the primary channel from the Gitea repo, and the DSN is compiled in anyway. RELEASE_DART_DEFINES: >- --dart-define=ENABLE_UPDATE_CHECK=true --dart-define=ENABLE_SENTRY=true --dart-define=GIT_COMMIT=${{ github.sha }} --dart-define=SENTRY_ENVIRONMENT=gitea-release --dart-define=ENABLE_DONATIONS=true jobs: build-windows: name: Build Windows x64 and publish to Gitea # Never queue on the Gitea instance (it has no Windows runner) and never # run in random forks (they lack GITEA_TOKEN). if: github.server_url == 'https://github.com' && github.repository == 'yorickr/plezy' runs-on: windows-latest permissions: contents: read steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 with: persist-credentials: false - name: Setup Flutter uses: subosito/flutter-action@1a449444c387b1966244ae4d4f8c696479add0b2 # v2 with: channel: "stable" flutter-version: ${{ env.FLUTTER_VERSION }} cache: true cache-key: "gitea-release-flutter-:os:-:channel:-:version:-:arch:-:hash:" pub-cache: false - name: Cache Pub dependencies uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6 with: path: | ~\AppData\Local\Pub\Cache key: gitea-release-${{ runner.os }}-${{ runner.arch }}-pub-${{ hashFiles('pubspec.yaml', 'pubspec.lock') }} - name: Install dependencies shell: pwsh run: flutter pub get --enforce-lockfile --no-example - name: Install patched Flutter engine (DComp) shell: pwsh run: | flutter precache --windows .\windows\tool\install-patched-engine.ps1 - name: Build Windows x64 shell: pwsh run: flutter build windows --release ${{ env.RELEASE_DART_DEFINES }} --dart-define=SENTRY_DIST=gitea-windows-x64 --split-debug-info=debug-info/windows-x64 - name: Upload symbols to bugs.plezy.app # Only when this fork has been given the admin token (the production # token lives on edde746/plezy, which is not the build bridge). # The secrets context is unavailable in `if`, so gate on the env copy. if: env.BUGS_ADMIN_TOKEN != '' shell: pwsh env: BUGS_ADMIN_TOKEN: ${{ secrets.BUGS_ADMIN_TOKEN }} SENTRY_DIST: gitea-windows-x64 run: .\scripts\upload-symbols.ps1 windows-x64 - name: Read version from pubspec id: version shell: pwsh run: | $v = (Select-String -Path pubspec.yaml -Pattern '^version:\s*(\S+)').Matches[0].Groups[1].Value -replace '\+.*' echo "version=$v" >> $env:GITHUB_OUTPUT - name: Build installer and portable shell: pwsh run: .\windows\build-installer.ps1 -X64BuildDir "build\windows\x64\runner\Release" -Version "${{ steps.version.outputs.version }}" - name: Sign installer for WinSparkle (EdDSA) if: env.SPARKLE_PRIVATE_KEY != '' env: SPARKLE_PRIVATE_KEY: ${{ secrets.SPARKLE_PRIVATE_KEY }} shell: pwsh run: | $keyPath = Join-Path $env:RUNNER_TEMP "plezy-winsparkle-ed25519.pem" try { Set-Content -Path $keyPath -Value $env:SPARKLE_PRIVATE_KEY -Encoding ascii -NoNewline $output = & dart run auto_updater:sign_update plezy-windows-installer.exe $keyPath if ($LASTEXITCODE -ne 0) { throw "WinSparkle signer failed with exit code $LASTEXITCODE" } $match = [regex]::Match($output, 'edSignature="([^"]*)"') if (-not $match.Success) { throw "WinSparkle signer returned no EdDSA signature" } Set-Content -Path win-ed-signature.txt -Value $match.Groups[1].Value -Encoding ascii -NoNewline Set-Content -Path win-installer-size.txt -Value (Get-Item plezy-windows-installer.exe).Length.ToString() -Encoding ascii -NoNewline } finally { Remove-Item -Path $keyPath -Force -ErrorAction SilentlyContinue } - name: Publish to Gitea release shell: bash env: GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }} GITEA_SERVER: ${{ vars.GITEA_SERVER || 'https://git.yorickrommers.nl' }} GITEA_REPO: ${{ vars.GITEA_REPO || 'yorickr/plezy' }} TAG: ${{ github.ref_name }} run: | set -euo pipefail if [[ -z "${GITEA_TOKEN}" ]]; then echo "Missing GITEA_TOKEN secret - cannot publish" >&2 exit 1 fi API="${GITEA_SERVER}/api/v1/repos/${GITEA_REPO}" VERSION="${{ steps.version.outputs.version }}" # Only publish tags that exist in the Gitea repo (i.e. tags mirrored # from there, or pushed to Gitea directly). Tags that only exist on # GitHub are not this pipeline's business. if ! curl -fsS -H "Authorization: token ${GITEA_TOKEN}" \ "${API}/git/refs/tags/${TAG}" >/dev/null 2>&1; then echo "Tag ${TAG} does not exist in ${GITEA_REPO} on ${GITEA_SERVER}; skipping publish" exit 0 fi NOTES=$(cat <<'EOF' Windows build for Plezy. Artifacts: - **plezy-windows-installer.exe** - Inno Setup installer (silent-update compatible, auto-detects architecture) - **plezy-windows-x64-portable.7z** - portable x64 build (no installation needed) Built automatically on GitHub-hosted runners from this tag and published to this Gitea release. EOF ) if RELEASE=$(curl -fsS -H "Authorization: token ${GITEA_TOKEN}" \ "${API}/releases/tags/${TAG}" 2>/dev/null); then RELEASE_ID=$(echo "${RELEASE}" | jq -r '.id') echo "Release for ${TAG} already exists (id ${RELEASE_ID}); updating" BODY=$(jq -n --arg name "Plezy ${VERSION}" --arg body "${NOTES}" \ '{name: $name, body: $body, draft: false, prerelease: false}') RESP=$(curl -fsS -X PATCH -H "Authorization: token ${GITEA_TOKEN}" \ -H "Content-Type: application/json" -d "${BODY}" \ "${API}/releases/${RELEASE_ID}") else echo "Creating release for ${TAG}" BODY=$(jq -n --arg tag "${TAG}" --arg name "Plezy ${VERSION}" --arg body "${NOTES}" \ '{tag_name: $tag, name: $name, body: $body, draft: false, prerelease: false}') RESP=$(curl -fsS -X POST -H "Authorization: token ${GITEA_TOKEN}" \ -H "Content-Type: application/json" -d "${BODY}" \ "${API}/releases") fi RELEASE_ID=$(echo "${RESP}" | jq -r '.id') if [[ -z "${RELEASE_ID}" || "${RELEASE_ID}" == "null" ]]; then echo "Failed to create/update the release:" >&2 echo "${RESP}" >&2 exit 1 fi # Upload each asset, replacing a same-named asset from an earlier run. upload() { local FILE="$1" local NAME NAME=$(basename "${FILE}") local EXISTING_ASSET EXISTING_ASSET=$(curl -fsS -H "Authorization: token ${GITEA_TOKEN}" \ "${API}/releases/${RELEASE_ID}/assets" | jq -r --arg name "${NAME}" \ '.[] | select(.name == $name) | .id') if [[ -n "${EXISTING_ASSET}" ]]; then curl -fsS -X DELETE -H "Authorization: token ${GITEA_TOKEN}" \ "${API}/releases/${RELEASE_ID}/assets/${EXISTING_ASSET}" >/dev/null fi curl -fsS -X POST -H "Authorization: token ${GITEA_TOKEN}" \ -F "attachment=@${FILE}" \ "${API}/releases/${RELEASE_ID}/assets?name=${NAME}" >/dev/null echo "Uploaded ${NAME} ($(stat -c%s "${FILE}") bytes)" } upload plezy-windows-x64-portable.7z upload plezy-windows-installer.exe echo "Published release for ${TAG}: ${GITEA_SERVER}/${GITEA_REPO}/releases/tag/${TAG}"