CI hardening: fast-fail check job (lockfile/tsc/eslint) gates the APK build + optional ntfy alerts; config plugin makes 'expo prebuild' preserve release signing/versioning; node 22 pinned with docker lock script + pre-push hook
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
59
app/plugins/withAndroidRelease.js
Normal file
59
app/plugins/withAndroidRelease.js
Normal file
@@ -0,0 +1,59 @@
|
||||
// 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;
|
||||
};
|
||||
Reference in New Issue
Block a user