ci(android): replace cargo-apk with cargo-ndk + manual APK assembly
Android Build / build-apk (push) Failing after 23m0s
Build and Deploy / build-and-push (push) Successful in 43s

cargo-apk 0.10 and its fork cargo-apk2 both failed to discover the
installed Android platform in this Gitea runner, despite ANDROID_HOME,
platforms;android-34, build-tools, and NDK all being present, readable,
and pointed at correctly. We never isolated whether the bug is in the
shared ndk-build crate's discovery logic or in the runner's env-var
propagation through cargo subcommand exec, so this commit stops fighting
either tool and assembles the APK from explicit toolchain steps instead:

  cargo ndk          -> per-ABI .so files
  aapt2 compile/link -> manifest + resources -> base APK
  zip                -> bundle native libs into lib/<abi>/
  zipalign           -> 4-byte alignment
  apksigner          -> v2/v3 signing (debug keystore for CI, real for release)

The pipeline lives in scripts/build_android_apk.sh so it's reproducible
locally (same env vars, same commands). AndroidManifest.xml is now
checked in under solitaire_app/android/ and mirrors what cargo-apk would
have generated from [package.metadata.android] — keep them in sync if
either is changed. Local `cargo apk build` still works on developer
machines where cargo-apk is happy; CI just stops depending on it.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
funman300
2026-05-14 11:53:55 -07:00
parent 14324b09ef
commit 7ee7cb6d93
4 changed files with 236 additions and 68 deletions
+51
View File
@@ -0,0 +1,51 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Mirrors what cargo-apk would generate from [package.metadata.android]
in solitaire_app/Cargo.toml. Kept in-tree so the CI workflow can drive
aapt2 directly without going through cargo-apk's brittle SDK discovery.
Keep in sync with:
* Cargo.toml: package, min_sdk_version, target_sdk_version,
uses_feature, uses_permission, application label/icon,
activity orientation
* [lib].name (currently "solitaire_app") — matches the
`android.app.lib_name` meta-data value below, which is the
shared object name without the `lib` prefix or `.so` suffix.
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.solitairequest.app"
android:versionCode="1"
android:versionName="1.0">
<uses-sdk
android:minSdkVersion="26"
android:targetSdkVersion="34" />
<uses-feature
android:name="android.hardware.touchscreen"
android:required="true" />
<uses-permission android:name="android.permission.INTERNET" />
<application
android:label="Ferrous Solitaire"
android:icon="@mipmap/ic_launcher"
android:hasCode="false">
<activity
android:name="android.app.NativeActivity"
android:exported="true"
android:screenOrientation="portrait"
android:configChanges="orientation|keyboardHidden|screenSize|keyboard|navigation|screenLayout">
<meta-data
android:name="android.app.lib_name"
android:value="solitaire_app" />
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>