Skip to content

Android Build and Release #8

Android Build and Release

Android Build and Release #8

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: Create GitHub Release (use Gradle version, commit history and commit id)
id: create_release
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
set -euo pipefail
# 1) 获取版本号(优先:app/build.gradle -> app/build.gradle.kts -> gradle.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}"}
# 2) commit id(短 sha)和 commit history 作为 body
COMMIT_ID=$(git rev-parse --short HEAD)
BODY=$(git log --pretty=format:"%h %ad - %s (%an)" --date=short --no-merges -n 200)
# 3) 构造 JSON payload(使用 jq 确保正确的 JSON 转义)
if ! command -v jq >/dev/null 2>&1; then
echo "jq is required but not installed in runner. Exiting."
exit 1
fi
PAYLOAD=$(jq -n --arg tag "${COMMIT_ID}" --arg name "${VERSION}" --arg body "${BODY}" \
'{ tag_name: $tag, name: $name, body: $body, draft: false, prerelease: false }')
# 4) 调用 GitHub Releases API 创建 release(创建时为最新)
RESPONSE=$(curl -s -X POST \
-H "Authorization: token ${GITHUB_TOKEN}" \
-H "Accept: application/vnd.github+json" \
-H "Content-Type: application/json" \
-d "${PAYLOAD}" \
"https://api.github.com/repos/${GITHUB_REPOSITORY}/releases")
# 输出响应以便日志可见
echo "GitHub release response:"
echo "${RESPONSE}" | jq .
# 5) 从响应中取 upload_url 并导出为步骤输出,供后续 upload-release-asset 使用
UPLOAD_URL=$(echo "${RESPONSE}" | jq -r '.upload_url // empty')
if [ -z "$UPLOAD_URL" ] || [ "$UPLOAD_URL" = "null" ]; then
echo "Failed to create release or upload_url missing. Full response above."
exit 1
fi
echo "upload_url=${UPLOAD_URL}" >> "$GITHUB_OUTPUT"
- name: Skip turnstyle (not required)
run: echo "Skipping turnstyle wrapper; uploading directly using upload-release-asset"
- name: Upload Debug APK to release
uses: actions/upload-release-asset@v1
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: release_artifacts/app-debug.apk
asset_name: app-debug.apk
asset_content_type: application/vnd.android.package-archive
- name: Upload Release APK to release
uses: actions/upload-release-asset@v1
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: release_artifacts/app-release.apk
asset_name: app-release.apk
asset_content_type: application/vnd.android.package-archive