# Build a signed release APK and publish it as a Forgejo release on every v* tag.
# Obtainium then tracks releases and offers the update.
#
# Requires:
#   - A Forgejo Actions RUNNER registered to this repo/instance (see docs/DISTRIBUTION.md),
#     with a label this job's `runs-on` matches (adjust below to your runner's labels).
#   - Repo Actions secrets: ANDROID_KEYSTORE_B64, ANDROID_KEYSTORE_PASSWORD,
#     ANDROID_KEY_ALIAS, ANDROID_KEY_PASSWORD (already set).
#
# Release: bump `expo.version` in app/app.json, then:  git tag v0.1.1 && git push origin v0.1.1

name: build-apk
on:
  push:
    tags:
      - 'v*'

jobs:
  build:
    # Match this to a label your runner registered with (commonly "docker" or "ubuntu-latest").
    runs-on: docker
    # A purpose-built RN Android image (Android SDK + NDK + Node). Runner needs Docker.
    container:
      image: reactnativecommunity/react-native-android:latest
      # Hard caps so the build can't starve the 20GB/4-core host: 2 CPUs, 8 GB RAM,
      # and NO container swap (--memory-swap == --memory) so it OOM-fails cleanly
      # instead of thrashing the disk with swap. With the JVM caps below it uses ~5 GB.
      options: --cpus=2 --memory=8g --memory-swap=8g
      # Persistent caches (whitelisted in the runner's config.yaml valid_volumes).
      # Gradle home = downloaded Maven deps + build cache; npm = package cache.
      # Repeat builds skip the re-download/re-compile that dominate a cold build.
      volumes:
        - bigbrainparking-gradle:/opt/cache/gradle
        - bigbrainparking-npm:/opt/cache/npm
    env:
      GRADLE_USER_HOME: /opt/cache/gradle
      npm_config_cache: /opt/cache/npm
      # Applies to EVERY JVM the build spawns (Gradle workers, artifact transforms,
      # Kotlin daemon) — the runner container has no IPv6 route, so without this the
      # transform workers try dl.google.com over IPv6 and get "Network is unreachable".
      JAVA_TOOL_OPTIONS: "-Djava.net.preferIPv4Stack=true"
    steps:
      - uses: actions/checkout@v4

      - name: Install dependencies
        run: npm install

      - name: Build the API client
        run: npm run build --workspace parksmarter-client

      # RN 0.79 wants a specific NDK; install it if the image doesn't ship it (best-effort).
      - name: Ensure NDK
        run: |
          yes | sdkmanager --licenses >/dev/null 2>&1 || true
          sdkmanager "ndk;27.1.12297006" >/dev/null 2>&1 || true

      - name: Restore signing keystore
        run: echo "${{ secrets.ANDROID_KEYSTORE_B64 }}" | base64 -d > "${{ github.workspace }}/app/release.keystore"

      - name: Expo prebuild (generate android/)
        working-directory: app
        run: CI=1 npx expo prebuild --platform android --no-install

      # The real memory hog is the Kotlin daemon (default ~5.4 GB heap EACH, several
      # spawned). Cap every JVM in gradle.properties so the build fits comfortably.
      - name: Cap Gradle/Kotlin memory
        working-directory: app/android
        run: |
          cat >> gradle.properties <<'PROPS'

          # --- CI resource caps (host is small; don't thrash swap) ---
          org.gradle.jvmargs=-Xmx1536m -XX:MaxMetaspaceSize=512m -Djava.net.preferIPv4Stack=true
          org.gradle.workers.max=2
          org.gradle.parallel=false
          org.gradle.daemon=false
          org.gradle.caching=true
          kotlin.daemon.jvmargs=-Xmx1536m
          PROPS

      - name: Assemble signed release
        working-directory: app/android
        env:
          BBP_UPLOAD_STORE_FILE: ${{ github.workspace }}/app/release.keystore
          BBP_UPLOAD_STORE_PASSWORD: ${{ secrets.ANDROID_KEYSTORE_PASSWORD }}
          BBP_UPLOAD_KEY_ALIAS: ${{ secrets.ANDROID_KEY_ALIAS }}
          BBP_UPLOAD_KEY_PASSWORD: ${{ secrets.ANDROID_KEY_PASSWORD }}
          # Also cap the no-daemon launcher JVM (org.gradle.jvmargs targets the daemon).
          GRADLE_OPTS: "-Xmx1536m -Djava.net.preferIPv4Stack=true"
        # --max-workers=2 limits concurrent tasks; container --cpus=2 caps clang -j.
        # --build-cache reuses task outputs from GRADLE_USER_HOME across runs.
        run: >-
          ./gradlew :app:assembleRelease
          -PreactNativeArchitectures=arm64-v8a
          --no-daemon --max-workers=2 --build-cache

      # Self-contained publish via the Forgejo API (no external action to fetch).
      - name: Publish release + upload APK
        env:
          TOKEN: ${{ secrets.GITHUB_TOKEN }}
        run: |
          set -e
          TAG="${GITHUB_REF_NAME}"
          APK="$(ls app/android/app/build/outputs/apk/release/*.apk | head -1)"
          ASSET="BigBrainParking-${TAG}.apk"
          API="${GITHUB_SERVER_URL}/api/v1/repos/${GITHUB_REPOSITORY}"
          REL="$(curl -sf -X POST "$API/releases" -H "Authorization: token $TOKEN" \
            -H "Content-Type: application/json" \
            -d "{\"tag_name\":\"$TAG\",\"target_commitish\":\"main\",\"name\":\"BigBrainParking $TAG\",\"body\":\"Install/update via Obtainium.\"}")"
          ID="$(printf '%s' "$REL" | node -e "let s='';process.stdin.on('data',d=>s+=d).on('end',()=>console.log(JSON.parse(s).id))")"
          curl -sf -X POST "$API/releases/$ID/assets?name=$ASSET" \
            -H "Authorization: token $TOKEN" -F "attachment=@$APK"
          echo "Published $TAG"
