Skip to content

Merge pull request #11 from tall-1997/fix/session-loss-and-crash #524

Merge pull request #11 from tall-1997/fix/session-loss-and-crash

Merge pull request #11 from tall-1997/fix/session-loss-and-crash #524

Workflow file for this run

name: Android APK
on:
push:
branches: [main]
tags:
- "v*"
- "*-linux"
workflow_dispatch:
permissions:
contents: write
concurrency:
group: android-apk-${{ github.ref }}
cancel-in-progress: true
env:
JAVA_VERSION: "17"
ANDROID_PLATFORM: "android-36"
ANDROID_BUILD_TOOLS: "35.0.0"
ANDROID_CMAKE: "3.22.1"
# Must match ndkVersion and minis-android-sdk-setup NDK_REV.
ANDROID_NDK: "29.0.14206865"
GRADLE_OPTS: "-Dorg.gradle.jvmargs=-Xmx6g -XX:MaxMetaspaceSize=1g"
jobs:
native-deps:
uses: ./.github/workflows/native-deps.yml
sandbox-assets:
uses: ./.github/workflows/sandbox-assets.yml
build:
name: Build Minis Ultra APK
needs: [native-deps, sandbox-assets]
runs-on: ubuntu-22.04
timeout-minutes: 180
steps:
- name: Checkout
uses: actions/checkout@v4
with:
submodules: recursive
fetch-depth: 0
- name: Set up JDK
uses: actions/setup-java@v4
with:
distribution: temurin
java-version: ${{ env.JAVA_VERSION }}
cache: gradle
- name: Restore Gradle caches
uses: actions/cache@v4
with:
path: |
~/.gradle/caches
~/.gradle/configuration-cache
key: gradle-${{ runner.os }}-${{ hashFiles('src/android/**/*.gradle.kts', 'src/android/gradle.properties', 'src/android/gradle/wrapper/gradle-wrapper.properties', 'src/android/build-logic/**/*.kt') }}
restore-keys: |
gradle-${{ runner.os }}-
- name: Install native build dependencies
run: |
sudo apt-get update
sudo DEBIAN_FRONTEND=noninteractive apt-get install -y curl tar unzip make gawk sed file python3 cmake ninja-build g++ git
- name: Install Android SDK commandline tools
run: |
mkdir -p /usr/local/lib/android/sdk/cmdline-tools
cd /tmp
curl -sL -o cmdline-tools.zip \
"https://dl.google.com/android/repository/commandlinetools-linux-11076708_latest.zip"
unzip -q cmdline-tools.zip -d /tmp/sdk
mv /tmp/sdk/cmdline-tools /usr/local/lib/android/sdk/cmdline-tools/latest
echo "ANDROID_SDK_ROOT=/usr/local/lib/android/sdk" >> "$GITHUB_ENV"
echo "ANDROID_HOME=/usr/local/lib/android/sdk" >> "$GITHUB_ENV"
yes | /usr/local/lib/android/sdk/cmdline-tools/latest/bin/sdkmanager --licenses >/dev/null || true
- name: Install Android SDK components
run: |
/usr/local/lib/android/sdk/cmdline-tools/latest/bin/sdkmanager \
"platform-tools" \
"platforms;${ANDROID_PLATFORM}" \
"build-tools;${ANDROID_BUILD_TOOLS}" \
"cmake;${ANDROID_CMAKE}" \
"ndk;${ANDROID_NDK}"
echo "ANDROID_NDK_HOME=/usr/local/lib/android/sdk/ndk/${ANDROID_NDK}" >> "$GITHUB_ENV"
echo "ANDROID_NDK_ROOT=/usr/local/lib/android/sdk/ndk/${ANDROID_NDK}" >> "$GITHUB_ENV"
- name: Download native dependencies
uses: actions/download-artifact@v4
with:
name: native-deps-${{ github.sha }}
path: /tmp/native-deps
- name: Install native dependencies
run: |
mkdir -p src/android/app/src/main/assets src/android/app/src/main/jniLibs/arm64-v8a src/android/app/libs
cp /tmp/native-deps/assets/proot-aarch64 src/android/app/src/main/assets/
cp /tmp/native-deps/jniLibs/arm64-v8a/libproot.so src/android/app/src/main/jniLibs/arm64-v8a/
cp /tmp/native-deps/libs/rclone.aar src/android/app/libs/rclone.aar
find /usr/local/lib/android/sdk/ndk/${ANDROID_NDK}/toolchains/llvm/prebuilt -path '*sysroot/usr/lib/aarch64-linux-android' -type d -print0 | while IFS= read -r -d '' d; do cp /tmp/native-deps/libunwind.a "$d/libunwind.a"; done
- name: Download sandbox assets
uses: actions/download-artifact@v4
with:
name: sandbox-assets-${{ github.sha }}
path: /tmp/sandbox-assets
- name: Install embedded sandbox assets
run: |
for f in ubuntu-base.tar.gz android-sdk-tools-aarch64.zip android-cmdline-tools.zip; do
src=$(find /tmp/sandbox-assets -name "$f" -type f -print -quit)
test -n "$src" && test -s "$src"
cp "$src" "src/android/app/src/main/assets/$f"
done
- name: Provider customization stub
run: |
test -f src/android/app/provider-customization.properties \
|| cp src/android/app/provider-customization.properties.example \
src/android/app/provider-customization.properties
- name: Upload keystore
env:
B64: ${{ secrets.MINIS_UPLOAD_KEYSTORE_BASE64 }}
run: |
if [ -n "$B64" ]; then
echo "$B64" | base64 -d > /tmp/minis-upload.jks
echo "MINIS_UPLOAD_STORE_FILE=/tmp/minis-upload.jks" >> "$GITHUB_ENV"
echo "MINIS_UPLOAD_STORE_PASSWORD=${{ secrets.MINIS_UPLOAD_STORE_PASSWORD }}" >> "$GITHUB_ENV"
echo "MINIS_UPLOAD_KEY_ALIAS=${{ secrets.MINIS_UPLOAD_KEY_ALIAS }}" >> "$GITHUB_ENV"
echo "MINIS_UPLOAD_KEY_PASSWORD=${{ secrets.MINIS_UPLOAD_KEY_PASSWORD }}" >> "$GITHUB_ENV"
echo "signed with GitHub Actions secret keystore"
elif [ -f src/android/release.keystore ] && [ -f src/android/signing.properties ]; then
echo "signed with committed src/android/release.keystore"
else
echo "No upload keystore — APK will be debug-signed (cannot replace a differently signed install)"
fi
- name: Assemble release APK
working-directory: src/android
env:
ANDROID_NDK_HOME: /usr/local/lib/android/sdk/ndk/${{ env.ANDROID_NDK }}
ANDROID_SDK_ROOT: /usr/local/lib/android/sdk
run: |
# gradle.properties is phone-tuned: 2g daemons + 1 worker. On a CI
# runner a cold full compile OOMs the Kotlin daemon at 2g (IR
# lowering, execIncrementalCompiler) and workers.max=1 neuters
# --parallel. Restore what every green run before cb6307f actually
# used: Kotlin daemon inheriting the 6g Gradle heap, 4 workers.
sed -i 's|org.gradle.jvmargs=.*|org.gradle.jvmargs=-Xmx6g -Dfile.encoding=UTF-8|' gradle.properties
sed -i 's|kotlin.daemon.jvmargs=.*|kotlin.daemon.jvmargs=-Xmx6g|' gradle.properties
sed -i 's|org.gradle.workers.max=.*|org.gradle.workers.max=4|' gradle.properties
chmod +x gradlew
# [T-ci-build-speed] The debug variant is disabled via androidComponents
# (app/build.gradle.kts), removing the kspDebug / kspRelease Room
# schema race. --parallel is safe and cuts build time.
./gradlew :app:assembleRelease --parallel --stacktrace
- name: Stage APK
run: |
SRC=src/android/app/build/outputs/apk/release/app-release.apk
test -s "$SRC"
mkdir -p dist
cp "$SRC" dist/minis-ultra-com.openminis.linux.apk
unzip -l "$SRC" | grep -E "libproot|proot-aarch64|ubuntu-base|android-sdk-tools|android-cmdline-tools" || true
NDK="/usr/local/lib/android/sdk/ndk/${ANDROID_NDK}"
UNWIND_SO=$(find "$NDK" -path '*aarch64-linux-android*' -name libunwind.so 2>/dev/null | head -n 1 || true)
UNWIND_A=$(find "$NDK" -path '*aarch64-linux-android*' -name libunwind.a 2>/dev/null | head -n 1 || true)
if [ -n "$UNWIND_SO" ]; then
cp "$UNWIND_SO" dist/libunwind.so
echo "staged libunwind.so from $UNWIND_SO"
fi
if [ -n "$UNWIND_A" ]; then
cp "$UNWIND_A" dist/libunwind.a
echo "staged libunwind.a from $UNWIND_A"
fi
if [ -z "$UNWIND_SO" ] && [ -z "$UNWIND_A" ]; then
echo "libunwind not in NDK sysroot — see docs/SIGNING.md"
fi
ls -lh dist/
- name: Stage mapping.txt and native symbols
run: |
mkdir -p dist/symbols
MAP=src/android/app/build/outputs/mapping/release/mapping.txt
if [ -f "$MAP" ]; then cp "$MAP" dist/symbols/mapping.txt; fi
find src/android/app/build -name 'native-debug-symbols.zip' -print -exec cp {} dist/symbols/ \; || true
MERGED=$(find src/android/app/build/intermediates -path '*merged_native_libs*release*' -name '*.so' | head -n 20)
if [ -n "$MERGED" ]; then
mkdir -p dist/symbols/so
for f in $MERGED; do cp "$f" dist/symbols/so/; done
fi
ls -lhR dist/symbols || true
- name: Upload APK artifact
uses: actions/upload-artifact@v4
with:
name: minis-ultra-apk
path: dist/minis-ultra-com.openminis.linux.apk
if-no-files-found: error
- name: Upload mapping and native symbols
uses: actions/upload-artifact@v4
with:
name: minis-ultra-symbols
path: dist/symbols
if-no-files-found: warn
- name: Read app version
id: appver
run: |
CODE=$(sed -n 's/.*versionCode = \([0-9][0-9]*\).*/\1/p' src/android/app/build.gradle.kts | head -1)
NAME=$(sed -n 's/.*versionName = "\(.*\)".*/\1/p' src/android/app/build.gradle.kts | head -1)
echo "code=$CODE" >> "$GITHUB_OUTPUT"
echo "name=$NAME" >> "$GITHUB_OUTPUT"
echo "app versionName=$NAME versionCode=$CODE"
- name: Publish rolling pre-release
if: github.ref == 'refs/heads/main'
uses: softprops/action-gh-release@v2
with:
tag_name: android-latest
name: Minis Ultra (Android)
prerelease: true
generate_release_notes: false
files: dist/*
body: |
来自 `${{ github.sha }}` 的滚动 Android 构建。正式版请看带版本号的 Release。
- 包名:`com.openminis.linux`
- 启动器名称:**Minis Ultra**
- 架构:arm64-v8a
- NDK:r29 `29.0.14206865`(与应用内安装同一修订号,不回退 r28)
- versionName:`${{ steps.appver.outputs.name }}`
- versionCode:${{ steps.appver.outputs.code }}
- 签名说明见 docs/SIGNING.md
- name: Select versioned release notes
if: startsWith(github.ref, 'refs/tags/')
id: notes
run: |
TAG="${GITHUB_REF_NAME}"
SHORT="${TAG%-linux}"
OUT="${RUNNER_TEMP}/RELEASE-BODY.md"
for f in "docs/github-release-${TAG}.md" "docs/github-release-${SHORT}.md"; do
if [ -f "$f" ]; then
cp "$f" "$OUT"
echo "path=$OUT" >> "$GITHUB_OUTPUT"
echo "release body: $f"
exit 0
fi
done
# RELEASE-NOTES.zh.md is the full changelog; never paste it wholesale.
awk 'BEGIN{p=1} /^---[[:space:]]*$/{exit} {print}' docs/RELEASE-NOTES.zh.md > "$OUT"
echo "path=$OUT" >> "$GITHUB_OUTPUT"
echo "release body: first section of docs/RELEASE-NOTES.zh.md"
# action-gh-release@v2 can exit 0 after creating an unpublished draft for a
# non-prerelease tag. The public Release then 404s and has no APK. Publish
# with gh and fail the job unless the APK is on a non-draft release.
- name: Publish versioned release
if: startsWith(github.ref, 'refs/tags/')
env:
GH_TOKEN: ${{ github.token }}
NOTES: ${{ steps.notes.outputs.path }}
run: |
set -euo pipefail
TAG="${GITHUB_REF_NAME}"
test -s "$NOTES"
mapfile -t ASSETS < <(find dist -maxdepth 1 -type f -size +0c | sort)
if [ "${#ASSETS[@]}" -eq 0 ]; then
echo "dist/ has no release files"
ls -la dist || true
exit 1
fi
echo "uploading: ${ASSETS[*]}"
if gh release view "$TAG" >/dev/null 2>&1; then
gh release upload "$TAG" "${ASSETS[@]}" --clobber
gh release edit "$TAG" --draft=false --prerelease=false --latest --notes-file "$NOTES"
else
gh release create "$TAG" "${ASSETS[@]}" --title "$TAG" --notes-file "$NOTES" --latest
fi
gh release view "$TAG" --json isDraft,assets --jq '{draft:.isDraft,assets:[.assets[].name]}'
gh release view "$TAG" --json isDraft,assets --jq 'select(.isDraft==false) | .assets[].name' \
| grep -qx 'minis-ultra-com.openminis.linux.apk'