Repository navigation
Mark Android app as a game #16
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: Build | |
| # Debug and release builds of the native ports (Linux, Windows, Android) for | |
| # every pushed commit, uploaded as artifacts. tools/ci_build.py does each | |
| # build; see it for the options. | |
| # | |
| # Kept cheap: | |
| # - one job per platform builds both configurations, so its set-up (tools, | |
| # downloads, caches) is paid once; | |
| # - compiles go through ccache, whose cache carries over from run to run, so | |
| # a commit only recompiles what it changed; downloads (SDL, musl, Gradle) | |
| # are cached too; | |
| # - a newer push to the same branch cancels a run still going; | |
| # - pushes that only change documentation do not build; | |
| # - Windows, whose minutes cost twice Linux's, builds only Windows; | |
| # - artifacts are kept for three days. | |
| # | |
| # A successful build of main is also published as a GitHub release, the | |
| # newest marked latest, so that | |
| # https://github.com/<repository>/releases/latest/download/<file> always | |
| # gives the latest build; the last five are kept. | |
| on: | |
| push: | |
| paths-ignore: | |
| - "**.md" | |
| - "docs/**" | |
| - "research/**" | |
| workflow_dispatch: | |
| concurrency: | |
| group: build-${{ github.ref }} | |
| cancel-in-progress: true | |
| permissions: | |
| contents: read | |
| env: | |
| CI_COMPILER_LAUNCHER: ccache | |
| CCACHE_DIR: ${{ github.workspace }}/.ccache | |
| CCACHE_BASEDIR: ${{ github.workspace }} | |
| CCACHE_MAXSIZE: 1G | |
| jobs: | |
| linux: | |
| # Arch Linux has what the build needs as packages: clang 22 (for the | |
| # profile-guided optimisation in pgo/) and 32-bit SDL 3. | |
| runs-on: ubuntu-24.04 | |
| container: archlinux:latest | |
| timeout-minutes: 40 | |
| steps: | |
| - name: Install the toolchain | |
| run: | | |
| printf '\n[multilib]\nInclude = /etc/pacman.d/mirrorlist\n' >> /etc/pacman.conf | |
| pacman -Syu --noconfirm --needed git zstd python python-pytest ninja ccache \ | |
| clang lld gcc lib32-glibc lib32-gcc-libs lib32-sdl3 | |
| - uses: actions/checkout@v4 | |
| - uses: actions/cache@v4 | |
| with: | |
| path: .ccache | |
| key: ccache-linux-${{ github.sha }} | |
| restore-keys: ccache-linux- | |
| - name: Debug | |
| run: python tools/ci_build.py linux debug | |
| - name: Release | |
| run: python tools/ci_build.py linux release | |
| - name: Test | |
| run: python -m pytest -q tools/test_linux_port.py | |
| - name: Compile cache | |
| run: ccache --show-stats | |
| - uses: actions/upload-artifact@v4 | |
| with: | |
| name: halo-linux-debug | |
| path: dist/halo-linux-debug | |
| retention-days: 3 | |
| - uses: actions/upload-artifact@v4 | |
| with: | |
| name: halo-linux-release | |
| path: dist/halo-linux-release | |
| retention-days: 3 | |
| android: | |
| # The runner image has the Android SDK and NDK, a JDK and CMake. | |
| runs-on: ubuntu-24.04 | |
| timeout-minutes: 40 | |
| steps: | |
| - name: Install the toolchain | |
| run: | | |
| mkdir -p "$RUNNER_TEMP/tools" | |
| curl -fsSL -o ninja.zip https://github.com/ninja-build/ninja/releases/download/v1.12.1/ninja-linux.zip | |
| unzip -q ninja.zip -d "$RUNNER_TEMP/tools" && rm ninja.zip | |
| curl -fsSL https://github.com/ccache/ccache/releases/download/v4.10.2/ccache-4.10.2-linux-x86_64.tar.xz \ | |
| | tar -xJ -C "$RUNNER_TEMP/tools" --strip-components=1 ccache-4.10.2-linux-x86_64/ccache | |
| echo "$RUNNER_TEMP/tools" >> "$GITHUB_PATH" | |
| - uses: actions/checkout@v4 | |
| - uses: actions/cache@v4 | |
| with: | |
| path: .ccache | |
| key: ccache-android-${{ github.sha }} | |
| restore-keys: ccache-android- | |
| # musl and SDL, which configure.py fetches | |
| - uses: actions/cache@v4 | |
| with: | |
| path: build/android/third_party | |
| key: android-third-party-${{ hashFiles('tools/android_build.py') }} | |
| - uses: actions/cache@v4 | |
| with: | |
| path: | | |
| ~/.gradle/caches | |
| ~/.gradle/wrapper | |
| key: gradle-${{ hashFiles('port/android/**/*.gradle*', 'port/android/gradle/wrapper/gradle-wrapper.properties') }} | |
| # the app's signing key, from the ANDROID_KEYSTORE_BASE64 secret (a | |
| # keystore, base64) and ANDROID_KEYSTORE_PASSWORD (its password, and | |
| # the key's, alias halo): every build signed with it installs over the | |
| # one before, as the app's self-updater (Updater.java) needs. Without | |
| # them each build is signed with the runner's own debug key. | |
| - name: Signing key | |
| env: | |
| KEYSTORE: ${{ secrets.ANDROID_KEYSTORE_BASE64 }} | |
| run: | | |
| if [ -n "$KEYSTORE" ]; then | |
| printf '%s' "$KEYSTORE" | base64 -d > "$RUNNER_TEMP/halo.keystore" | |
| echo "HALO_ANDROID_KEYSTORE=$RUNNER_TEMP/halo.keystore" >> "$GITHUB_ENV" | |
| else | |
| echo "::warning::no ANDROID_KEYSTORE_BASE64 secret: the Android builds cannot update over each other" | |
| fi | |
| - name: Debug | |
| env: | |
| HALO_ANDROID_KEYSTORE_PASSWORD: ${{ secrets.ANDROID_KEYSTORE_PASSWORD }} | |
| run: python3 tools/ci_build.py android debug | |
| - name: Release | |
| env: | |
| HALO_ANDROID_KEYSTORE_PASSWORD: ${{ secrets.ANDROID_KEYSTORE_PASSWORD }} | |
| run: python3 tools/ci_build.py android release | |
| - name: Compile cache | |
| run: ccache --show-stats | |
| - uses: actions/upload-artifact@v4 | |
| with: | |
| name: halo-android-debug | |
| path: dist/halo-android-debug | |
| retention-days: 3 | |
| - uses: actions/upload-artifact@v4 | |
| with: | |
| name: halo-android-release | |
| path: dist/halo-android-release | |
| retention-days: 3 | |
| windows: | |
| # The runner image has LLVM, Python and Visual Studio's x86 libraries and | |
| # Windows SDK. | |
| runs-on: windows-2025 | |
| timeout-minutes: 40 | |
| defaults: | |
| run: | |
| shell: bash | |
| steps: | |
| - name: Install the toolchain | |
| shell: pwsh | |
| run: | | |
| Invoke-WebRequest -OutFile ninja.zip https://github.com/ninja-build/ninja/releases/download/v1.12.1/ninja-win.zip | |
| Expand-Archive ninja.zip -DestinationPath "$env:RUNNER_TEMP\tools" | |
| Invoke-WebRequest -OutFile ccache.zip https://github.com/ccache/ccache/releases/download/v4.10.2/ccache-4.10.2-windows-x86_64.zip | |
| Expand-Archive ccache.zip -DestinationPath $env:RUNNER_TEMP | |
| Copy-Item "$env:RUNNER_TEMP\ccache-4.10.2-windows-x86_64\ccache.exe" "$env:RUNNER_TEMP\tools" | |
| Remove-Item ninja.zip, ccache.zip | |
| "$env:RUNNER_TEMP\tools" | Out-File -Append -Encoding utf8 $env:GITHUB_PATH | |
| - uses: actions/checkout@v4 | |
| - uses: actions/cache@v4 | |
| with: | |
| path: .ccache | |
| key: ccache-windows-${{ github.sha }} | |
| restore-keys: ccache-windows- | |
| # SDL's development package, which configure.py fetches | |
| - uses: actions/cache@v4 | |
| with: | |
| path: build/windows/third_party | |
| key: windows-third-party-${{ hashFiles('tools/windows_build.py') }} | |
| - name: Debug | |
| run: python tools/ci_build.py windows debug | |
| - name: Release | |
| run: python tools/ci_build.py windows release | |
| - name: Compile cache | |
| run: ccache --show-stats | |
| - uses: actions/upload-artifact@v4 | |
| with: | |
| name: halo-windows-debug | |
| path: dist/halo-windows-debug | |
| retention-days: 3 | |
| - uses: actions/upload-artifact@v4 | |
| with: | |
| name: halo-windows-release | |
| path: dist/halo-windows-release | |
| retention-days: 3 | |
| release: | |
| # The six builds as a release, tagged build-<run number> and marked latest | |
| # (every file keeps its name from release to release, for the README's | |
| # latest/download links); then all but the newest five build-* releases | |
| # go, tags and all. Only for main, and only when every platform built. | |
| needs: [linux, android, windows] | |
| if: ${{ github.ref == 'refs/heads/main' }} | |
| runs-on: ubuntu-24.04 | |
| timeout-minutes: 15 | |
| permissions: | |
| contents: write | |
| outputs: | |
| tag: ${{ steps.publish.outputs.tag }} | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| GH_REPO: ${{ github.repository }} | |
| steps: | |
| - uses: actions/download-artifact@v4 | |
| with: | |
| pattern: halo-* | |
| path: artifacts | |
| - name: Package | |
| run: | | |
| mkdir release | |
| for build in artifacts/*/; do | |
| name="$(basename "$build")" | |
| (cd "$build" && zip -qr "$GITHUB_WORKSPACE/release/$name.zip" .) | |
| done | |
| ls -l release | |
| - name: Publish | |
| id: publish | |
| env: | |
| MESSAGE: ${{ github.event.head_commit.message }} | |
| COMMIT_URL: ${{ github.server_url }}/${{ github.repository }}/commit/${{ github.sha }} | |
| RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} | |
| run: | | |
| tag="build-$GITHUB_RUN_NUMBER" | |
| subject="$(printf '%s' "${MESSAGE:-manual run}" | head -n 1)" | |
| printf '%s\n\nCommit: %s\nBuild: %s\n\nThe release builds are for playing; the debug builds stop at the first failed assertion and log it.\n' \ | |
| "$subject" "$COMMIT_URL" "$RUN_URL" > notes.md | |
| gh release create "$tag" release/*.zip --target "$GITHUB_SHA" --latest \ | |
| --title "Build $GITHUB_RUN_NUMBER (${GITHUB_SHA:0:7})" --notes-file notes.md | |
| echo "tag=$tag" >> "$GITHUB_OUTPUT" | |
| - name: Keep the last five | |
| run: | | |
| gh release list --limit 100 --json tagName,createdAt \ | |
| --jq '[.[] | select(.tagName | startswith("build-"))] | sort_by(.createdAt) | reverse | .[5:] | .[].tagName' | | |
| while read -r tag; do | |
| gh release delete "$tag" --cleanup-tag --yes | |
| done | |
| notify: | |
| # A Discord embed for the run, to the DISCORD_WEBHOOK secret (nothing | |
| # without it): green when every platform built, red when one did not, | |
| # with each platform's result, this run's release (main only), and links | |
| # to the latest release's builds. | |
| needs: [linux, android, windows, release] | |
| if: ${{ !cancelled() }} | |
| runs-on: ubuntu-24.04 | |
| timeout-minutes: 5 | |
| steps: | |
| - name: Post to Discord | |
| env: | |
| WEBHOOK: ${{ secrets.DISCORD_WEBHOOK }} | |
| LINUX: ${{ needs.linux.result }} | |
| ANDROID: ${{ needs.android.result }} | |
| WINDOWS: ${{ needs.windows.result }} | |
| RELEASE_TAG: ${{ needs.release.outputs.tag }} | |
| SERVER: ${{ github.server_url }} | |
| REPOSITORY: ${{ github.repository }} | |
| BRANCH: ${{ github.ref_name }} | |
| SHA: ${{ github.sha }} | |
| ACTOR: ${{ github.actor }} | |
| MESSAGE: ${{ github.event.head_commit.message }} | |
| RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} | |
| COMMIT_URL: ${{ github.server_url }}/${{ github.repository }}/commit/${{ github.sha }} | |
| run: | | |
| if [ -z "$WEBHOOK" ]; then | |
| echo "no DISCORD_WEBHOOK secret: nothing to post" | |
| exit 0 | |
| fi | |
| status() { | |
| case "$1" in | |
| success) echo "✅ Built" ;; | |
| failure) echo "❌ Failed" ;; | |
| cancelled) echo "⏹️ Cancelled" ;; | |
| *) echo "➖ Skipped" ;; | |
| esac | |
| } | |
| if [ "$LINUX" = success ] && [ "$ANDROID" = success ] && [ "$WINDOWS" = success ]; then | |
| title="Build succeeded"; color=5763719 | |
| else | |
| title="Build failed"; color=15548997 | |
| fi | |
| latest="$SERVER/$REPOSITORY/releases/latest" | |
| downloads="[Linux]($latest/download/halo-linux-release.zip) · [Windows]($latest/download/halo-windows-release.zip) · [Android]($latest/download/halo-android-release.zip) ([all builds]($latest))" | |
| if [ -n "$RELEASE_TAG" ]; then | |
| release="[$RELEASE_TAG]($SERVER/$REPOSITORY/releases/tag/$RELEASE_TAG)" | |
| else | |
| release="None (only a successful build of main makes one)" | |
| fi | |
| subject="$(printf '%s' "${MESSAGE:-manual run}" | head -n 1 | cut -c1-200)" | |
| jq -n \ | |
| --arg title "$title" --argjson color "$color" --arg url "$RUN_URL" \ | |
| --arg description "[\`${SHA:0:7}\`]($COMMIT_URL) $subject — $ACTOR ($BRANCH)" \ | |
| --arg release "$release" --arg downloads "$downloads" \ | |
| --arg linux "$(status "$LINUX")" --arg windows "$(status "$WINDOWS")" --arg android "$(status "$ANDROID")" \ | |
| --arg timestamp "$(date -u +%Y-%m-%dT%H:%M:%SZ)" \ | |
| '{embeds: [{ | |
| title: $title, url: $url, color: $color, description: $description, timestamp: $timestamp, | |
| fields: [ | |
| {name: "Linux", value: $linux, inline: true}, | |
| {name: "Windows", value: $windows, inline: true}, | |
| {name: "Android", value: $android, inline: true}, | |
| {name: "Release", value: $release}, | |
| {name: "Latest release builds", value: $downloads} | |
| ] | |
| }]}' > payload.json | |
| curl -fsS -H "Content-Type: application/json" -d @payload.json "$WEBHOOK" |