From a037be8d26cd8674ac6cf4ae92943a1fe01255b7 Mon Sep 17 00:00:00 2001 From: Boof2015 <75185879+Boof2015@users.noreply.github.com> Date: Thu, 13 Aug 2026 14:34:08 -0400 Subject: [PATCH] fix metaspace oom for ci --- .github/workflows/android-candidate.yml | 2 + plugins/withAstraAndroidRelease.js | 43 +++++++++++++ plugins/withAstraAndroidRelease.test.mjs | 80 ++++++++++++++++++++++++ 3 files changed, 125 insertions(+) diff --git a/.github/workflows/android-candidate.yml b/.github/workflows/android-candidate.yml index 8b14c1d..4617475 100644 --- a/.github/workflows/android-candidate.yml +++ b/.github/workflows/android-candidate.yml @@ -96,6 +96,7 @@ jobs: grep -Fq "ASTRA VENDORED KOTLIN AUDIO" android/settings.gradle grep -Fq "ASTRA KOTLIN AUDIO SUBSTITUTION" android/build.gradle grep -Fq "ASTRA RELEASE SIGNING" android/app/build.gradle + grep -Fq 'org.gradle.jvmargs=-Xmx2048m -XX:MaxMetaspaceSize=1024m' android/gradle.properties grep -Fq 'android:shell="true"' android/app/src/main/AndroidManifest.xml - name: Decode app-signing keystore @@ -218,6 +219,7 @@ jobs: grep -Fq "ASTRA VENDORED KOTLIN AUDIO" android/settings.gradle grep -Fq "ASTRA KOTLIN AUDIO SUBSTITUTION" android/build.gradle grep -Fq "ASTRA RELEASE SIGNING" android/app/build.gradle + grep -Fq 'org.gradle.jvmargs=-Xmx2048m -XX:MaxMetaspaceSize=1024m' android/gradle.properties grep -Fq 'android:shell="true"' android/app/src/main/AndroidManifest.xml - name: Run Astra native unit tests diff --git a/plugins/withAstraAndroidRelease.js b/plugins/withAstraAndroidRelease.js index 9a75d86..b1b2ee3 100644 --- a/plugins/withAstraAndroidRelease.js +++ b/plugins/withAstraAndroidRelease.js @@ -3,6 +3,7 @@ const path = require('path'); const { AndroidConfig, withDangerousMod, + withGradleProperties, withAndroidManifest, withAppBuildGradle, withProjectBuildGradle, @@ -15,6 +16,8 @@ const SIGNING_MARKER = '// ASTRA RELEASE SIGNING'; const CAMERA_FEATURE = 'android.hardware.camera'; const NOTIFICATION_ICON_FILE = 'astra_notification_icon.xml'; const NOTIFICATION_OVERRIDE_FILE = 'astra_notification_icon_overrides.xml'; +const GRADLE_JVM_ARGS_PROPERTY = 'org.gradle.jvmargs'; +const GRADLE_JVM_ARGS_VALUE = '-Xmx2048m -XX:MaxMetaspaceSize=1024m'; // A notification small icon is an alpha mask: Android supplies the color for // the status bar, lock screen and media controls. Keep only the main Astra mark @@ -108,6 +111,31 @@ function appendBlock(contents, marker, block) { return contents.includes(marker) ? contents : `${contents.trimEnd()}${block}`; } +function upsertGradleProperty(properties, key, value) { + let propertyFound = false; + const updatedProperties = []; + + for (const property of properties) { + if (property.type === 'property' && property.key === key) { + if (!propertyFound) { + updatedProperties.push({ ...property, value }); + propertyFound = true; + } + continue; + } + updatedProperties.push(property); + } + + if (!propertyFound) { + if (updatedProperties.length > 0 && updatedProperties.at(-1)?.type !== 'empty') { + updatedProperties.push({ type: 'empty' }); + } + updatedProperties.push({ type: 'property', key, value }); + } + + return updatedProperties; +} + function addReleaseSigning(contents) { if (contents.includes(SIGNING_MARKER)) return contents; @@ -159,6 +187,17 @@ function withReleaseSigning(config) { }); } +function withGradleBuildMemory(config) { + return withGradleProperties(config, (mod) => { + mod.modResults = upsertGradleProperty( + mod.modResults, + GRADLE_JVM_ARGS_PROPERTY, + GRADLE_JVM_ARGS_VALUE + ); + return mod; + }); +} + function withProfileableRelease(config) { return withAndroidManifest(config, (mod) => { AndroidConfig.Manifest.ensureToolsAvailable(mod.modResults); @@ -224,6 +263,7 @@ function withAstraNotificationIcon(config) { function withAstraAndroidRelease(config) { config = withVendoredKotlinAudio(config); config = withReleaseSigning(config); + config = withGradleBuildMemory(config); config = withAstraNotificationIcon(config); return withProfileableRelease(config); } @@ -233,12 +273,15 @@ module.exports._internal = { PROJECT_BUILD_MARKER, SETTINGS_MARKER, SIGNING_MARKER, + GRADLE_JVM_ARGS_PROPERTY, + GRADLE_JVM_ARGS_VALUE, NOTIFICATION_ICON_FILE, NOTIFICATION_ICON_VECTOR, NOTIFICATION_OVERRIDE_FILE, NOTIFICATION_ICON_OVERRIDE, addReleaseSigning, appendBlock, + upsertGradleProperty, ensureOptionalCameraFeature, writeNotificationIconResources, }; diff --git a/plugins/withAstraAndroidRelease.test.mjs b/plugins/withAstraAndroidRelease.test.mjs index d6a61ae..51222c0 100644 --- a/plugins/withAstraAndroidRelease.test.mjs +++ b/plugins/withAstraAndroidRelease.test.mjs @@ -53,6 +53,86 @@ test('appendBlock adds a native block exactly once', () => { assert.equal(once.split(_internal.SETTINGS_MARKER).length - 1, 1); }); +test('raises the generated Gradle Metaspace cap while preserving unrelated properties', () => { + const properties = [ + { type: 'comment', value: 'Project-wide Gradle settings.' }, + { + type: 'property', + key: _internal.GRADLE_JVM_ARGS_PROPERTY, + value: '-Xmx2048m -XX:MaxMetaspaceSize=512m', + }, + { type: 'property', key: 'org.gradle.parallel', value: 'true' }, + ]; + + assert.deepEqual( + _internal.upsertGradleProperty( + properties, + _internal.GRADLE_JVM_ARGS_PROPERTY, + _internal.GRADLE_JVM_ARGS_VALUE + ), + [ + properties[0], + { + type: 'property', + key: _internal.GRADLE_JVM_ARGS_PROPERTY, + value: '-Xmx2048m -XX:MaxMetaspaceSize=1024m', + }, + properties[2], + ] + ); +}); + +test('adds the Gradle JVM arguments when the generated property is absent', () => { + const transformed = _internal.upsertGradleProperty( + [{ type: 'property', key: 'org.gradle.parallel', value: 'true' }], + _internal.GRADLE_JVM_ARGS_PROPERTY, + _internal.GRADLE_JVM_ARGS_VALUE + ); + + assert.deepEqual(transformed.slice(-2), [ + { type: 'empty' }, + { + type: 'property', + key: _internal.GRADLE_JVM_ARGS_PROPERTY, + value: _internal.GRADLE_JVM_ARGS_VALUE, + }, + ]); +}); + +test('Gradle JVM argument transform is idempotent and removes duplicate properties', () => { + const properties = [ + { + type: 'property', + key: _internal.GRADLE_JVM_ARGS_PROPERTY, + value: '-Xmx2048m -XX:MaxMetaspaceSize=512m', + }, + { + type: 'property', + key: _internal.GRADLE_JVM_ARGS_PROPERTY, + value: '-Xmx1024m -XX:MaxMetaspaceSize=256m', + }, + ]; + const once = _internal.upsertGradleProperty( + properties, + _internal.GRADLE_JVM_ARGS_PROPERTY, + _internal.GRADLE_JVM_ARGS_VALUE + ); + const twice = _internal.upsertGradleProperty( + once, + _internal.GRADLE_JVM_ARGS_PROPERTY, + _internal.GRADLE_JVM_ARGS_VALUE + ); + + assert.deepEqual(twice, once); + assert.equal( + once.filter( + (property) => + property.type === 'property' && property.key === _internal.GRADLE_JVM_ARGS_PROPERTY + ).length, + 1 + ); +}); + test('marks QR scanning camera hardware as optional without duplicating it', () => { const manifest = { manifest: {} };