Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
124 changes: 106 additions & 18 deletions .github/workflows/build-android.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,11 @@ jobs:
- name: Install required Android SDK packages
run: sdkmanager "platforms;android-36" "build-tools;36.0.0"

- name: ci init
run: npm config set legacy-peer-deps true

- name: Install root dependencies (electerm + build tooling)
run: npm ci
run: npm i

- name: Install Capacitor dependencies
run: npm --prefix build/android install
Expand All @@ -51,40 +54,125 @@ jobs:

- name: Ensure nodejs-mobile gradle variable is defined
# @capawesome/capacitor-nodejs expects $nodejsMobileVersion to be set.
# Append on its own line (variables.gradle has no trailing newline, so a
# plain `echo >>` would concatenate onto the closing `}` and break Gradle).
run: |
cd build/android/android
grep -q "nodejsMobileVersion" variables.gradle || \
echo "nodejsMobileVersion = '18.20.4-capawesome.1'" >> variables.gradle
printf '\next.nodejsMobileVersion = "18.20.4-capawesome.1"\n' >> variables.gradle

- name: Apply icon / splash overlay
run: cp -r build/android/res-overlay/. build/android/android/app/src/main/res/
run: |
# AndroidManifest.xml must go at app/src/main/, NOT inside res/.
# Copy the electerm manifest (with icon, theme, network config) to
# the correct location, overwriting Capacitor's default.
MANIFEST_SRC=build/android/res-overlay/AndroidManifest.xml
MANIFEST_DST=build/android/android/app/src/main/AndroidManifest.xml
if [ -f "$MANIFEST_SRC" ]; then
cp "$MANIFEST_SRC" "$MANIFEST_DST"
echo "Overlayed AndroidManifest.xml -> $MANIFEST_DST"
fi

# Copy all resource dirs (drawable, mipmap-*, values, xml) into res/.
# Using overlay/. merges directory contents into the destination
# without creating extra nesting levels.
cp -r build/android/res-overlay/. build/android/android/app/src/main/res/

# Remove Capacitor's default resources that conflict with / shadow
# the electerm overlay. These are generated by `cap add android` and
# must be deleted AFTER the overlay copy:
# drawable-v24/ic_launcher_foreground.xml — vector foreground that
# takes precedence over our drawable/ic_launcher_foreground.png
# on API 24+, showing the generic Capacitor logo instead of electerm.
# values/ic_launcher_background.xml — same color name as our
# colors-electerm.xml → duplicate-resource build error.
# drawable/splash.png — same resource name as our splash.xml.
# mipmap-*/ic_launcher_round.png + mipmap-anydpi-v26/ic_launcher_round.xml
# — Capacitor's default round icons; remove so only electerm icons remain.
RES=build/android/android/app/src/main/res
rm -f "$RES/drawable-v24/ic_launcher_foreground.xml"
rm -f "$RES/values/ic_launcher_background.xml"
rm -f "$RES/drawable/splash.png"
rm -f "$RES"/mipmap-*/ic_launcher_round.png
rm -f "$RES/mipmap-anydpi-v26/ic_launcher_round.xml"

- name: Configure per-ABI APK splits
# Produce one APK per architecture (arm64-v8a, armeabi-v7a, x86_64)
# plus a universal build, instead of a single ~160MB universal blob.
# The second `android { }` block is cumulative with Capacitor's
# template app/build.gradle; output renaming is done post-build in
# shell to avoid AGP-version-specific output-name APIs.
run: |
GRADLE=build/android/android/app/build.gradle
grep -q 'splits' "$GRADLE" || printf '%s\n' \
'' \
'// electerm: split per ABI (injected by CI)' \
'android {' \
' splits {' \
' abi {' \
' enable true' \
' reset()' \
" include 'arm64-v8a', 'armeabi-v7a', 'x86_64'" \
' universalApk true' \
' }' \
' }' \
'}' >> "$GRADLE"
tail -n 15 "$GRADLE"

- name: Build debug APK
run: cd build/android/android && chmod +x gradlew && ./gradlew assembleDebug

- name: Build release APK (signed with ephemeral keystore)
- name: Build release APK (signed with persistent keystore)
env:
KEYSTORE_PASSWORD: ${{ secrets.ANDROID_KEYSTORE_PASSWORD || 'android' }}
KEY_PASSWORD: ${{ secrets.ANDROID_KEY_PASSWORD || 'android' }}
ANDROID_RELEASE_KEYSTORE_BASE64: ${{ secrets.ANDROID_RELEASE_KEYSTORE_BASE64 }}
ANDROID_RELEASE_KEYSTORE_PASSWORD: ${{ secrets.ANDROID_RELEASE_KEYSTORE_PASSWORD }}
ANDROID_RELEASE_KEY_PASSWORD: ${{ secrets.ANDROID_RELEASE_KEY_PASSWORD }}
ANDROID_RELEASE_KEY_ALIAS: ${{ secrets.ANDROID_RELEASE_KEY_ALIAS }}
run: |
cd build/android/android
keytool -genkey -v \
-keystore release.keystore -alias electerm \
-keyalg RSA -keysize 2048 -validity 10000 \
-storepass "$KEYSTORE_PASSWORD" -keypass "$KEY_PASSWORD" \
-dname "CN=electerm"
KEYSTORE="$PWD/app/release.keystore"
printf '%s' "$ANDROID_RELEASE_KEYSTORE_BASE64" | base64 --decode > "$KEYSTORE"
keytool -list -keystore "$KEYSTORE" -storepass "$ANDROID_RELEASE_KEYSTORE_PASSWORD"
./gradlew assembleRelease \
-Pandroid.injected.signing.store.file=release.keystore \
-Pandroid.injected.signing.store.password="$KEYSTORE_PASSWORD" \
-Pandroid.injected.signing.key.alias=electerm \
-Pandroid.injected.signing.key.password="$KEY_PASSWORD"
-Pandroid.injected.signing.store.file="$KEYSTORE" \
-Pandroid.injected.signing.store.password="$ANDROID_RELEASE_KEYSTORE_PASSWORD" \
-Pandroid.injected.signing.key.alias="$ANDROID_RELEASE_KEY_ALIAS" \
-Pandroid.injected.signing.key.password="$ANDROID_RELEASE_KEY_PASSWORD"

- name: Rename APKs to electerm-android-{arch}-{version}.apk
# Version comes from package.json. Each per-ABI APK is renamed to
# electerm-android-{arch}-{version}.apk (release) /
# electerm-android-{arch}-{version}-debug.apk (debug). The {arch} is
# parsed from gradle's per-ABI output filename; falls back to
# "universal" for the universal build (or if splits are off).
run: |
VERSION=$(node -p "require('./package.json').version")
APKROOT=build/android/android/app/build/outputs/apk
mkdir -p "$APKROOT/named"
for variant in debug release; do
dir="$APKROOT/$variant"
[ -d "$dir" ] || continue
for apk in "$dir"/*.apk; do
[ -f "$apk" ] || continue
name=$(basename "$apk")
arch=$(printf '%s' "$name" | grep -oE 'arm64-v8a|armeabi-v7a|x86_64|universal' | head -1)
[ -z "$arch" ] && arch=universal
if [ "$variant" = release ]; then
out="electerm-android-$arch-$VERSION.apk"
else
out="electerm-android-$arch-$VERSION-debug.apk"
fi
cp "$apk" "$APKROOT/named/$out"
echo " $variant/$name -> named/$out"
done
done
echo "=== named APKs ==="
ls -la "$APKROOT/named/"

- name: Upload APKs
uses: actions/upload-artifact@v4
with:
name: electerm-android-apks
path: |
build/android/android/app/build/outputs/apk/debug/*.apk
build/android/android/app/build/outputs/apk/release/*.apk
path: build/android/android/app/build/outputs/apk/named/*.apk
if-no-files-found: error
retention-days: 30
Loading
Loading