// Makes `expo prebuild` safe to re-run: re-applies the hand-maintained Android // release config that a fresh prebuild would otherwise reset to template // defaults (android/ is committed; CI builds it and relies on all of this). const { withAppBuildGradle, withGradleProperties } = require("expo/config-plugins"); const RELEASE_SIGNING = ` release { // Keystore path + password come from env (CI) or ~/.android-keys (local). def ksPath = System.getenv("KEYSTORE_FILE") ?: "\${System.properties['user.home']}/.android-keys/gtd-release.jks" def passFile = new File("\${System.properties['user.home']}/.android-keys/gtd-release.password") def ksPass = System.getenv("KEYSTORE_PASSWORD") ?: (passFile.exists() ? passFile.text.trim() : null) if (new File(ksPath).exists() && ksPass != null) { storeFile file(ksPath) storePassword ksPass keyAlias System.getenv("KEY_ALIAS") ?: "gtd" keyPassword System.getenv("KEY_PASSWORD") ?: ksPass } } `; function patchBuildGradle(gradle) { gradle = gradle.replace(/versionCode \d+\n/, 'versionCode ((System.getenv("VERSION_CODE") ?: "1").toInteger())\n'); gradle = gradle.replace(/versionName "[^"]*"\n/, 'versionName (System.getenv("VERSION_NAME") ?: "1.0-dev")\n'); if (!gradle.includes("KEYSTORE_FILE")) { // Insert the release signing config right after the debug one. gradle = gradle.replace( /(signingConfigs \{\n debug \{[\s\S]*?\n \}\n)/, `$1${RELEASE_SIGNING}`, ); } // Release builds use the release key when present, debug key otherwise. gradle = gradle.replace( /(release \{\n)(?:\s*\/\/[^\n]*\n)*\s*signingConfig signingConfigs\.debug\n/, "$1 signingConfig signingConfigs.release.storeFile ? signingConfigs.release : signingConfigs.debug\n", ); return gradle; } const GRADLE_PROPS = [ // RN/NDK builds want headroom; the template default is 2 GB. { key: "org.gradle.jvmargs", value: "-Xmx8192m -XX:MaxMetaspaceSize=1024m" }, // arm64 only: Pixel is arm64-v8a; x86/v7a builds are wasted time + APK bloat. { key: "reactNativeArchitectures", value: "arm64-v8a" }, ]; module.exports = function withAndroidRelease(config) { config = withAppBuildGradle(config, (c) => { c.modResults.contents = patchBuildGradle(c.modResults.contents); return c; }); config = withGradleProperties(config, (c) => { for (const { key, value } of GRADLE_PROPS) { const existing = c.modResults.find((p) => p.type === "property" && p.key === key); if (existing) existing.value = value; else c.modResults.push({ type: "property", key, value }); } return c; }); return config; };