Nightly Builds #70
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Nightly Builds | |
| on: | |
| schedule: | |
| - cron: '0 6 * * *' | |
| workflow_dispatch: | |
| permissions: | |
| contents: read | |
| jobs: | |
| metadata: | |
| name: Nightly Metadata | |
| runs-on: ubuntu-latest | |
| outputs: | |
| base_version: ${{ steps.version.outputs.base_version }} | |
| version_iteration: ${{ steps.version.outputs.iteration }} | |
| version: ${{ steps.version.outputs.version }} | |
| version_tag: ${{ steps.version.outputs.version_tag }} | |
| release_tag: ${{ steps.version.outputs.release_tag }} | |
| release_name: ${{ steps.version.outputs.release_name }} | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v6 | |
| - name: Setup Python | |
| uses: actions/setup-python@v6 | |
| with: | |
| python-version: '3.x' | |
| - name: Compute nightly version | |
| id: version | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| nightly_date="$(date -u +%Y%m%d)" | |
| run_id="${GITHUB_RUN_NUMBER}.${GITHUB_RUN_ATTEMPT}" | |
| python tools/build/openq4_version.py \ | |
| --source-root . \ | |
| --track nightly \ | |
| --iteration "${nightly_date}.${run_id}" >> "$GITHUB_OUTPUT" | |
| version_tag="$(grep '^version_tag=' "$GITHUB_OUTPUT" | tail -n 1 | cut -d= -f2-)" | |
| release_tag="nightly-${version_tag}" | |
| release_name="OpenQ4 Nightly ${version_tag}" | |
| echo "release_tag=${release_tag}" >> "$GITHUB_OUTPUT" | |
| echo "release_name=${release_name}" >> "$GITHUB_OUTPUT" | |
| nightly: | |
| name: ${{ matrix.label }} | |
| needs: metadata | |
| runs-on: ${{ matrix.os }} | |
| env: | |
| OPENQ4_GAMELIBS_REPO: ${{ github.workspace }}/OpenQ4-GameLibs | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| - os: windows-latest | |
| platform: windows | |
| label: Windows | |
| binary_arch: x64 | |
| platform_backend: sdl3 | |
| archive_format: zip | |
| archive_ext: .zip | |
| - os: ubuntu-latest | |
| platform: linux | |
| label: Linux | |
| binary_arch: x64 | |
| platform_backend: sdl3 | |
| archive_format: tar.xz | |
| archive_ext: .tar.xz | |
| - os: macos-latest | |
| platform: macos | |
| label: macOS | |
| binary_arch: arm64 | |
| platform_backend: sdl3 | |
| archive_format: tar.gz | |
| archive_ext: .tar.gz | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v6 | |
| - name: Fetch OpenQ4-GameLibs | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| git clone --depth 1 https://github.com/themuffinator/OpenQ4-GameLibs.git OpenQ4-GameLibs | |
| - name: Setup Python | |
| uses: actions/setup-python@v6 | |
| with: | |
| python-version: '3.x' | |
| - name: Install Meson and Ninja | |
| run: | | |
| python -m pip install --upgrade pip | |
| python -m pip install meson ninja | |
| - name: Compute nightly version | |
| id: version_echo | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| echo "Nightly version: ${{ needs.metadata.outputs.version }}" | |
| echo "Nightly version tag: ${{ needs.metadata.outputs.version_tag }}" | |
| - name: Install Linux native dependencies | |
| if: matrix.platform == 'linux' | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| sudo apt-get update | |
| sudo apt-get install -y \ | |
| libglew-dev \ | |
| libopenal-dev \ | |
| libx11-dev \ | |
| libxext-dev \ | |
| libxxf86vm-dev | |
| - name: Install macOS native dependencies | |
| if: matrix.platform == 'macos' | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| brew install glew | |
| - name: Build and install (Windows) | |
| if: matrix.platform == 'windows' | |
| shell: pwsh | |
| run: | | |
| $ErrorActionPreference = "Stop" | |
| $env:OPENQ4_SKIP_GAMELIBS_SYNC = "1" | |
| powershell -ExecutionPolicy Bypass -File tools/build/meson_setup.ps1 setup --wipe builddir . --backend ninja --buildtype=release --wrap-mode=forcefallback "-Dplatform_backend=${{ matrix.platform_backend }}" "-Dversion_track=nightly" "-Dversion_iteration=${{ needs.metadata.outputs.version_iteration }}" | |
| if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } | |
| powershell -ExecutionPolicy Bypass -File tools/build/meson_setup.ps1 compile -C builddir | |
| if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } | |
| powershell -ExecutionPolicy Bypass -File tools/build/meson_setup.ps1 install -C builddir --no-rebuild --skip-subprojects | |
| if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } | |
| - name: Build and install (Linux/macOS) | |
| if: matrix.platform != 'windows' | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| export OPENQ4_SKIP_GAMELIBS_SYNC=1 | |
| bash tools/build/meson_setup.sh setup --wipe builddir . --backend ninja --buildtype=release --wrap-mode=default -Dplatform_backend=${{ matrix.platform_backend }} -Dversion_track=nightly -Dversion_iteration=${{ needs.metadata.outputs.version_iteration }} | |
| bash tools/build/meson_setup.sh compile -C builddir | |
| bash tools/build/meson_setup.sh install -C builddir --no-rebuild --skip-subprojects | |
| - name: Validate staged payload | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| if [ ! -d ".install/openq4" ]; then | |
| echo "Staged openq4 payload directory is missing." | |
| exit 1 | |
| fi | |
| has_payload_files="$(find ".install/openq4" -type f -print -quit 2>/dev/null || true)" | |
| if [ -z "${has_payload_files}" ]; then | |
| echo "Staged openq4 payload directory has no files." | |
| exit 1 | |
| fi | |
| if [ "${{ matrix.platform }}" = "linux" ]; then | |
| if [ ! -f ".install/share/applications/openq4.desktop" ]; then | |
| echo "Missing Linux desktop entry in staged payload." | |
| exit 1 | |
| fi | |
| if [ ! -f ".install/share/icons/hicolor/256x256/apps/openq4.png" ]; then | |
| echo "Missing Linux 256x256 icon in staged payload." | |
| exit 1 | |
| fi | |
| fi | |
| - name: Prepare package | |
| id: package | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| python tools/build/package_nightly.py \ | |
| --platform "${{ matrix.platform }}" \ | |
| --arch "${{ matrix.binary_arch }}" \ | |
| --version "${{ needs.metadata.outputs.version }}" \ | |
| --version-tag "${{ needs.metadata.outputs.version_tag }}" \ | |
| --archive-format "${{ matrix.archive_format }}" \ | |
| --source-root "${GITHUB_WORKSPACE}" \ | |
| --output-dir "${RUNNER_TEMP}" | |
| archive_path="${RUNNER_TEMP}/openq4-${{ needs.metadata.outputs.version_tag }}-${{ matrix.platform }}${{ matrix.archive_ext }}" | |
| package_dir="${RUNNER_TEMP}/openq4-${{ needs.metadata.outputs.version_tag }}-${{ matrix.platform }}" | |
| if [ ! -f "${archive_path}" ]; then | |
| echo "Expected package archive not found: ${archive_path}" | |
| exit 1 | |
| fi | |
| if [ ! -d "${package_dir}" ]; then | |
| echo "Expected package directory not found: ${package_dir}" | |
| exit 1 | |
| fi | |
| echo "archive_path=${archive_path}" >> "$GITHUB_OUTPUT" | |
| echo "package_dir=${package_dir}" >> "$GITHUB_OUTPUT" | |
| - name: Validate macOS app bundle | |
| if: matrix.platform == 'macos' | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| app_root="${{ steps.package.outputs.package_dir }}/OpenQ4.app" | |
| app_plist="${app_root}/Contents/Info.plist" | |
| app_exec="${app_root}/Contents/MacOS/OpenQ4" | |
| app_icon="${app_root}/Contents/Resources/OpenQ4.icns" | |
| if [ ! -f "${app_plist}" ]; then | |
| echo "Missing macOS app Info.plist: ${app_plist}" | |
| exit 1 | |
| fi | |
| if [ ! -x "${app_exec}" ]; then | |
| echo "Missing or non-executable macOS app launcher: ${app_exec}" | |
| exit 1 | |
| fi | |
| if [ ! -f "${app_icon}" ]; then | |
| echo "Missing macOS app icon: ${app_icon}" | |
| exit 1 | |
| fi | |
| - name: Upload artifact | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: openq4-nightly-${{ needs.metadata.outputs.version_tag }}-${{ matrix.platform }} | |
| path: ${{ steps.package.outputs.archive_path }} | |
| if-no-files-found: error | |
| release: | |
| name: Publish Nightly Release | |
| needs: [metadata, nightly] | |
| if: ${{ needs.nightly.result == 'success' }} | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: write | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v6 | |
| with: | |
| fetch-depth: 0 | |
| - name: Fetch tags | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| git fetch --force --tags origin | |
| - name: Setup Python | |
| uses: actions/setup-python@v6 | |
| with: | |
| python-version: '3.x' | |
| - name: Download packaged artifacts | |
| uses: actions/download-artifact@v4 | |
| with: | |
| pattern: openq4-nightly-${{ needs.metadata.outputs.version_tag }}-* | |
| path: ${{ runner.temp }}/nightly-artifacts | |
| merge-multiple: true | |
| - name: Verify platform archives | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| artifact_dir="${RUNNER_TEMP}/nightly-artifacts" | |
| if [ ! -d "${artifact_dir}" ]; then | |
| echo "Artifact directory is missing: ${artifact_dir}" | |
| exit 1 | |
| fi | |
| ls -lah "${artifact_dir}" | |
| expected=( | |
| "openq4-${{ needs.metadata.outputs.version_tag }}-windows.zip" | |
| "openq4-${{ needs.metadata.outputs.version_tag }}-linux.tar.xz" | |
| "openq4-${{ needs.metadata.outputs.version_tag }}-macos.tar.gz" | |
| ) | |
| missing=0 | |
| for package_file in "${expected[@]}"; do | |
| if [ ! -f "${artifact_dir}/${package_file}" ]; then | |
| echo "Missing expected package archive: ${package_file}" | |
| missing=1 | |
| fi | |
| done | |
| if [ "${missing}" -ne 0 ]; then | |
| exit 1 | |
| fi | |
| - name: Generate changelog | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| python tools/build/generate_nightly_changelog.py \ | |
| --version "${{ needs.metadata.outputs.version }}" \ | |
| --version-tag "${{ needs.metadata.outputs.version_tag }}" \ | |
| --release-tag "${{ needs.metadata.outputs.release_tag }}" \ | |
| --repo "${GITHUB_REPOSITORY}" \ | |
| --run-id "${GITHUB_RUN_ID}" \ | |
| --run-url "${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}" \ | |
| --output "${RUNNER_TEMP}/nightly-changelog.md" | |
| - name: Create or update nightly release | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| tag="${{ needs.metadata.outputs.release_tag }}" | |
| name="${{ needs.metadata.outputs.release_name }}" | |
| notes="${RUNNER_TEMP}/nightly-changelog.md" | |
| assets_dir="${RUNNER_TEMP}/nightly-artifacts" | |
| mapfile -t assets < <(find "${assets_dir}" -maxdepth 1 -type f | sort) | |
| if [ "${#assets[@]}" -eq 0 ]; then | |
| echo "No release assets were downloaded." | |
| exit 1 | |
| fi | |
| if gh release view "${tag}" --repo "${GITHUB_REPOSITORY}" >/dev/null 2>&1; then | |
| gh release edit "${tag}" \ | |
| --repo "${GITHUB_REPOSITORY}" \ | |
| --title "${name}" \ | |
| --notes-file "${notes}" \ | |
| --latest=false | |
| gh release upload "${tag}" "${assets[@]}" --repo "${GITHUB_REPOSITORY}" --clobber | |
| else | |
| gh release create "${tag}" "${assets[@]}" \ | |
| --repo "${GITHUB_REPOSITORY}" \ | |
| --title "${name}" \ | |
| --notes-file "${notes}" \ | |
| --latest=false | |
| fi | |
| release_id="$(gh api "repos/${GITHUB_REPOSITORY}/releases/tags/${tag}" --jq .id)" | |
| gh api \ | |
| --method PATCH \ | |
| "repos/${GITHUB_REPOSITORY}/releases/${release_id}" \ | |
| -f prerelease=false \ | |
| -f draft=false >/dev/null |