CampgroundTickets/.forgejo/workflows/build-apk.yml
Hank f14e45349e CI: cap runner parallelism; play success chime immediately on scan
CI: limit Gradle to 2 worker JVMs (org.gradle.workers.max=2, --max-workers=2,
parallel=false), cap native compiler jobs (CMAKE_BUILD_PARALLEL_LEVEL=2), and
build only the arm64-v8a ABI — so the build no longer spawns a JVM/compiler per
core and swamps the host (which also serves Forgejo).

App: the success chime now fires the instant a scan resolves to a valid ticket
(ticket/ice modes), matching Banquet, instead of after the check-in round-trip.
The failure sound is unchanged. Removed the duplicate chime at check-in.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-08 17:04:52 +00:00

127 lines
5.4 KiB
YAML

name: Build Android APK
on:
push:
tags:
- "v*"
workflow_dispatch:
jobs:
build-apk:
runs-on: docker
container:
image: node:22-bookworm
# Persistent caches across runs (Docker named volumes). These must be
# allowed in the runner's config.yaml `container.valid_volumes` — the
# deploy-runner.sh script sets that up. First run populates them (~1h);
# later runs reuse the SDK/NDK, Gradle deps + build cache, and npm cache,
# dropping the build to a few minutes.
volumes:
- camptickets-android-sdk:/opt/android-sdk
- camptickets-gradle:/root/.gradle
- camptickets-npm:/root/.npm
env:
ANDROID_HOME: /opt/android-sdk
ANDROID_SDK_ROOT: /opt/android-sdk
GRADLE_USER_HOME: /root/.gradle
# Force IPv4 for all JVMs (Gradle launcher, daemon, Kotlin/CMake workers).
# The runner host has no working IPv6 route, so Maven Central (which has
# AAAA records) was unreachable — this makes Java ignore AAAA and use IPv4.
JAVA_TOOL_OPTIONS: -Djava.net.preferIPv4Stack=true
# Keep the build from swamping the host:
# - workers.max=2: at most 2 concurrent Gradle worker JVMs (was one per core)
# - parallel=false: don't build projects in parallel
# - caching=true: reuse the local build cache (persisted in GRADLE_USER_HOME)
GRADLE_OPTS: >-
-Dorg.gradle.jvmargs=-Xmx3g
-Dorg.gradle.daemon=false
-Dorg.gradle.caching=true
-Dorg.gradle.parallel=false
-Dorg.gradle.workers.max=2
# Cap the C/C++ (ninja) compiler jobs per native-build task to 2.
CMAKE_BUILD_PARALLEL_LEVEL: "2"
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Resolve version from tag
id: ver
run: |
TAG="${GITHUB_REF_NAME:-v0.0.0}"
echo "tag=$TAG" >> "$GITHUB_OUTPUT"
node ci/set-version.mjs "$TAG"
- name: Install JDK 17 and tools
run: |
apt-get update
apt-get install -y --no-install-recommends openjdk-17-jdk-headless unzip wget git
echo "JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64" >> "$GITHUB_ENV"
- name: Install Android SDK (cached)
run: |
set -eu
# Skip the whole install when the cache volume already has the SDK.
# (NDK + CMake are auto-installed by Gradle into the same volume on
# the first build, so they persist too.)
if [ ! -x "$ANDROID_HOME/cmdline-tools/latest/bin/sdkmanager" ]; then
echo "Installing Android command-line tools..."
mkdir -p "$ANDROID_HOME/cmdline-tools"
cd /tmp
wget -q https://dl.google.com/android/repository/commandlinetools-linux-11076708_latest.zip -O cmdtools.zip
unzip -q cmdtools.zip -d "$ANDROID_HOME/cmdline-tools"
mv "$ANDROID_HOME/cmdline-tools/cmdline-tools" "$ANDROID_HOME/cmdline-tools/latest"
else
echo "Android SDK found in cache volume — skipping download."
fi
export PATH="$ANDROID_HOME/cmdline-tools/latest/bin:$PATH"
yes | sdkmanager --licenses >/dev/null 2>&1 || true
# sdkmanager is a no-op for packages already present in the volume.
sdkmanager --install "platform-tools" \
"platforms;android-36" "platforms;android-35" \
"build-tools;36.0.0" "build-tools;35.0.0" >/dev/null
echo "$ANDROID_HOME/platform-tools" >> "$GITHUB_PATH"
echo "$ANDROID_HOME/cmdline-tools/latest/bin" >> "$GITHUB_PATH"
- name: Install app dependencies
working-directory: app
run: npm install --no-audit --no-fund
- name: Expo prebuild (android)
working-directory: app
run: npx expo prebuild -p android --no-install
- name: Decode release keystore
run: |
echo "${{ secrets.ANDROID_KEYSTORE_B64 }}" | base64 -d > "$RUNNER_TEMP/release.keystore"
- name: Build release APK
working-directory: app/android
env:
CAMPSCAN_STORE_FILE: ${{ runner.temp }}/release.keystore
CAMPSCAN_STORE_PASSWORD: ${{ secrets.ANDROID_KEYSTORE_PASSWORD }}
CAMPSCAN_KEY_ALIAS: ${{ secrets.ANDROID_KEY_ALIAS }}
CAMPSCAN_KEY_PASSWORD: ${{ secrets.ANDROID_KEY_PASSWORD }}
run: |
chmod +x ./gradlew
# Build a single ABI (arm64-v8a covers all modern phones) to cut native
# compilation ~4x and keep the host load down. --max-workers=2 bounds
# concurrent tasks on top of the workers.max setting.
./gradlew assembleRelease \
--init-script ../../ci/signing.gradle \
--no-daemon --build-cache --max-workers=2 \
-PreactNativeArchitectures=arm64-v8a
mkdir -p "$GITHUB_WORKSPACE/artifacts"
cp app/build/outputs/apk/release/app-release.apk \
"$GITHUB_WORKSPACE/artifacts/camp-scan-${{ steps.ver.outputs.tag }}.apk"
- name: Publish Forgejo release with APK
uses: actions/forgejo-release@v2
with:
direction: upload
url: https://git.mowden.top
repo: Beartaria/CampgroundTickets
tag: ${{ steps.ver.outputs.tag }}
token: ${{ secrets.GITHUB_TOKEN }}
release-dir: artifacts
release-notes: "Camp Scan ${{ steps.ver.outputs.tag }} — install/update via Obtainium."
override: true