/* Injects a release signing config into the prebuilt android project. Keystore: $HEALTH_KEYSTORE or ~/.android-keys/health-app-release.jks Password: $KEYSTORE_PASSWORD or ~/.android-keys/health-app-release.password Falls back to the debug keystore when neither exists. */ const { withAppBuildGradle, withGradleProperties } = require('expo/config-plugins'); const fs = require('fs'); const os = require('os'); const path = require('path'); const SIGNING = ` release { // keystore lives outside the repo; password from env or ~/.android-keys def ksPath = System.getenv("HEALTH_KEYSTORE") ?: "\${System.properties['user.home']}/.android-keys/health-app-release.jks" def passFile = new File("\${System.properties['user.home']}/.android-keys/health-app-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") ?: "health" keyPassword ksPass } } }`; /* RN's CMake tooling breaks on JDK 24+ (native-access restrictions); pin a local JDK 17–21 for Gradle when one is present under ~/.jdks. */ function findJdk21() { const dir = path.join(os.homedir(), '.jdks'); if (!fs.existsSync(dir)) return null; const hit = fs .readdirSync(dir) .filter((d) => /^jdk-(17|21)/.test(d)) .sort() .pop(); return hit ? path.join(dir, hit) : null; } module.exports = function withReleaseSigning(config) { config = withGradleProperties(config, (cfg) => { const jdk = findJdk21(); if (jdk && !cfg.modResults.some((p) => p.key === 'org.gradle.java.home')) { cfg.modResults.push({ type: 'property', key: 'org.gradle.java.home', value: jdk }); } /* use every core: parallel module builds + build cache */ const set = (key, value) => { const hit = cfg.modResults.find((p) => p.key === key); if (hit) hit.value = value; else cfg.modResults.push({ type: 'property', key, value }); }; set('org.gradle.parallel', 'true'); set('org.gradle.caching', 'true'); set('org.gradle.configuration-cache', 'false'); set('org.gradle.workers.max', String(os.cpus().length)); set('org.gradle.jvmargs', '-Xmx6g -XX:MaxMetaspaceSize=1g'); return cfg; }); return withAppBuildGradle(config, (cfg) => { let g = cfg.modResults.contents; if (!g.includes('def ksPath')) { g = g.replace( /(signingConfigs \{[\s\S]*?keyPassword 'android'\n {8}\}\n)( {4}\})/, `$1${SIGNING}`, ); g = g.replace( /\/\/ Caution![^\n]*\n\s*\/\/ see https:\/\/reactnative\.dev[^\n]*\n\s*signingConfig signingConfigs\.debug/, 'signingConfig signingConfigs.release.storeFile ? signingConfigs.release : signingConfigs.debug', ); cfg.modResults.contents = g; } return cfg; }); };