Without a consistent signing key, each CI build was signed with the
ephemeral debug keystore from the Flutter container — causing Android
to reject updates with INSTALL_FAILED_UPDATE_INCOMPATIBLE.
Secrets stored in repo: RELEASE_KEYSTORE_BASE64, RELEASE_KEYSTORE_PASSWORD,
RELEASE_KEY_ALIAS. CI decodes the keystore and writes key.properties
before building so every release APK is signed identically.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Configure release signing via key.properties + Gradle Kotlin DSL
- Add INTERNET and ACCESS_NETWORK_STATE permissions to AndroidManifest
(Flutter injects INTERNET automatically in debug builds but not release,
causing DNS failures in sideloaded APKs)
- Restore android/.gitignore entries lost during signing setup
- Gitignore key.properties and fabled-release-key.jks
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>