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