Android Build and Release #18
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: | |
| release_notes: | |
| description: '发布注释(可选,支持多行)' | |
| required: false | |
| default: '' | |
| type: string | |
| 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: Init submodules (llama.cpp) | |
| run: | | |
| git submodule update --init --depth 1 | |
| - 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: Accept Android SDK licenses | |
| run: | | |
| # 自动查找 sdkmanager 的位置并接受许可 | |
| SDK_MANAGER=$(find $ANDROID_HOME -name sdkmanager | head -n 1) | |
| if [ -n "$SDK_MANAGER" ]; then | |
| echo "Found sdkmanager at $SDK_MANAGER" | |
| yes | $SDK_MANAGER --licenses || true | |
| else | |
| echo "sdkmanager not found in $ANDROID_HOME" | |
| # 尝试直接使用环境变量中可能存在的路径 | |
| yes | sdkmanager --licenses || true | |
| fi | |
| - 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 | |
| # 搜集所有生成的 APK (包括不同架构和 universal 包) | |
| find app/build/outputs/apk/ -name "*.apk" -exec cp {} release_artifacts/ \; | |
| # 检查是否搜集到了文件 | |
| COUNT=$(ls release_artifacts/*.apk | wc -l) | |
| if [ "$COUNT" -eq 0 ]; then | |
| echo "No APKs found. Listing outputs for debugging:" | |
| ls -R app/build/outputs/apk/ || true | |
| exit 1 | |
| fi | |
| echo "Found $COUNT APKs." | |
| - name: Compute release metadata | |
| id: meta | |
| env: | |
| RELEASE_NOTES: ${{ github.event.inputs.release_notes }} | |
| 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) | |
| # Ensure tag name is version with a leading 'v' (e.g., v0.0.1) | |
| case "$VERSION" in | |
| v*|V*) TAG_NAME="$VERSION" ;; | |
| *) TAG_NAME="v$VERSION" ;; | |
| esac | |
| # Prefer user-provided release notes from workflow dispatch input; fallback to git log | |
| if [ -n "${RELEASE_NOTES:-}" ]; then | |
| BODY="$RELEASE_NOTES" | |
| else | |
| BODY=$(git log --pretty=format:"%h %ad - %s (%an)" --date=short --no-merges -n 200) | |
| fi | |
| echo "tag_name=${TAG_NAME}" >> "$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/*.apk |