Android Build and Release #10
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: Android Build and Release | |
| on: | |
| workflow_dispatch: | |
| inputs: {} | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| permissions: | |
| contents: write | |
| jobs: | |
| build: | |
| name: Build APKs and publish Release | |
| runs-on: ubuntu-latest | |
| env: | |
| JAVA_HOME_21_X64: /usr/lib/jvm/java-21-openjdk-amd64 | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| - name: Set up JDK 21 | |
| uses: actions/setup-java@v4 | |
| with: | |
| java-version: '21' | |
| distribution: 'temurin' | |
| - name: Cache Gradle | |
| uses: actions/cache@v4 | |
| with: | |
| path: | | |
| ~/.gradle/caches | |
| ~/.gradle/wrapper/ | |
| key: gradle-cache-${{ runner.os }}-v1 | |
| - name: Ensure Gradle wrapper version | |
| run: | | |
| ./gradlew --version || true | |
| # If you want to force wrapper update add a step to set distributionUrl in gradle/wrapper/gradle-wrapper.properties | |
| - name: Grant execute permission for gradlew | |
| run: chmod +x ./gradlew | |
| - name: Build Debug APK | |
| run: | | |
| # Set JVM max heap via GRADLE_OPTS to avoid shell/PowerShell parsing issues with -Porg.gradle.jvmargs | |
| GRADLE_OPTS="-Xmx3g" ./gradlew assembleDebug | |
| - name: Decode keystore (if provided) | |
| shell: bash | |
| env: | |
| RELEASE_KEYSTORE_BASE64: ${{ secrets.RELEASE_KEYSTORE_BASE64 }} | |
| run: | | |
| set -euo pipefail | |
| if [ -n "${RELEASE_KEYSTORE_BASE64:-}" ]; then | |
| mkdir -p app/release | |
| echo "$RELEASE_KEYSTORE_BASE64" | base64 --decode > "$PWD/app/release/release-keystore.jks" | |
| ls -l "$PWD/app/release/release-keystore.jks" | |
| else | |
| echo "RELEASE_KEYSTORE_BASE64 not set; skipping keystore decode" | |
| fi | |
| - name: Build Release APK | |
| env: | |
| RELEASE_KEYSTORE_BASE64: ${{ secrets.RELEASE_KEYSTORE_BASE64 }} | |
| RELEASE_KEYSTORE_KEY: ${{ secrets.RELEASE_KEYSTORE_KEY }} | |
| RELEASE_KEYSTORE_SUBKEY: ${{ secrets.RELEASE_KEYSTORE_SUBKEY }} | |
| run: | | |
| set -euo pipefail | |
| # Set JVM max heap via GRADLE_OPTS to avoid shell/PowerShell parsing issues with -Porg.gradle.jvmargs | |
| GRADLE_OPTS="-Xmx3g" | |
| # If keystore secret is present but file missing, decode it now (defensive) | |
| if [ -n "${RELEASE_KEYSTORE_BASE64:-}" ] && [ ! -f "$PWD/app/release/release-keystore.jks" ]; then | |
| mkdir -p app/release | |
| echo "$RELEASE_KEYSTORE_BASE64" | base64 --decode > "$PWD/app/release/release-keystore.jks" | |
| chmod 600 "$PWD/app/release/release-keystore.jks" | |
| fi | |
| # Build with signing properties if keystore exists and passwords provided | |
| STORE_FILE="$PWD/app/release/release-keystore.jks" | |
| if [ -f "$STORE_FILE" ] && [ -n "${RELEASE_KEYSTORE_KEY:-}" ] && [ -n "${RELEASE_KEYSTORE_SUBKEY:-}" ]; then | |
| echo "Keystore found — building signed release APK" | |
| ./gradlew assembleRelease -Pandroid.injected.signing.store.file="$STORE_FILE" -Pandroid.injected.signing.store.password="${RELEASE_KEYSTORE_KEY}" -Pandroid.injected.signing.key.password="${RELEASE_KEYSTORE_SUBKEY}" -Pandroid.injected.signing.key.alias=release | |
| else | |
| echo "No keystore/passwords provided — building release APK without injected signing properties" | |
| ./gradlew assembleRelease | |
| fi | |
| - name: Prepare artifacts | |
| run: | | |
| set -euo pipefail | |
| mkdir -p release_artifacts | |
| # Standard apk paths | |
| DEBUG_APK=app/build/outputs/apk/debug/app-debug.apk | |
| RELEASE_APK=app/build/outputs/apk/release/app-release.apk | |
| COPIED=0 | |
| if [ -f "$DEBUG_APK" ]; then | |
| cp "$DEBUG_APK" release_artifacts/app-debug.apk | |
| COPIED=$((COPIED+1)) | |
| else | |
| echo "Debug APK not found at $DEBUG_APK" | |
| fi | |
| if [ -f "$RELEASE_APK" ]; then | |
| cp "$RELEASE_APK" release_artifacts/app-release.apk | |
| COPIED=$((COPIED+1)) | |
| else | |
| echo "Release APK not found at $RELEASE_APK" | |
| fi | |
| if [ $COPIED -ne 2 ]; then | |
| echo "Missing apk. Listing outputs for debugging:" | |
| ls -R app/build || true | |
| fi | |
| - name: Compute release metadata | |
| id: meta | |
| run: | | |
| set -euo pipefail | |
| # Derive version (Gradle file preferred, fallback to properties) | |
| VERSION="" | |
| if [ -f app/build.gradle ]; then | |
| VERSION=$(grep -m1 "versionName" app/build.gradle | sed -E "s/.*versionName[[:space:]]+['\"]([^'\"]+)['\"].*/\1/" || true) | |
| fi | |
| if [ -z "$VERSION" ] && [ -f app/build.gradle.kts ]; then | |
| VERSION=$(grep -m1 "versionName" app/build.gradle.kts | sed -E "s/.*versionName\\s*=?\\s*\"([^\"]+)\".*/\1/" || true) | |
| fi | |
| if [ -z "$VERSION" ] && [ -f gradle.properties ]; then | |
| VERSION=$(grep -m1 -E "^(VERSION_NAME|versionName|VERSION)=" gradle.properties | cut -d'=' -f2- | tr -d '[:space:]' || true) | |
| fi | |
| VERSION=${VERSION:-"build-${GITHUB_RUN_ID}"} | |
| COMMIT_ID=$(git rev-parse --short HEAD) | |
| BODY=$(git log --pretty=format:"%h %ad - %s (%an)" --date=short --no-merges -n 200) | |
| echo "tag_name=${COMMIT_ID}" >> "$GITHUB_OUTPUT" | |
| echo "release_name=${VERSION}" >> "$GITHUB_OUTPUT" | |
| { | |
| echo 'body<<EOF' | |
| echo "$BODY" | |
| echo EOF | |
| } >> "$GITHUB_OUTPUT" | |
| - name: Create GitHub Release and upload APKs | |
| uses: softprops/action-gh-release@v1 | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| with: | |
| tag_name: ${{ steps.meta.outputs.tag_name }} | |
| name: ${{ steps.meta.outputs.release_name }} | |
| body: ${{ steps.meta.outputs.body }} | |
| fail_on_unmatched_files: false | |
| files: | | |
| release_artifacts/app-debug.apk | |
| release_artifacts/app-release.apk |