From 62d0e1c1dfe29f5c110a4bad18866b1a27e95812 Mon Sep 17 00:00:00 2001 From: artus9033 Date: Thu, 25 Jun 2026 22:27:16 +0200 Subject: [PATCH 1/5] fix: brownfield Gradle Plugin not to depend on *UpdatesResources task from expo-updates if absent --- .changeset/fair-swans-remain.md | 5 +++++ .../brownfield/processors/VariantTaskProvider.kt | 15 ++++++++++++--- 2 files changed, 17 insertions(+), 3 deletions(-) create mode 100644 .changeset/fair-swans-remain.md diff --git a/.changeset/fair-swans-remain.md b/.changeset/fair-swans-remain.md new file mode 100644 index 00000000..dcf51ab7 --- /dev/null +++ b/.changeset/fair-swans-remain.md @@ -0,0 +1,5 @@ +--- +'@callstack/react-native-brownfield': patch +--- + +fix: brownfield Gradle Plugin not to depend on \*UpdatesResources task from expo-updates if it is absent diff --git a/gradle-plugins/react/brownfield/src/main/kotlin/com/callstack/react/brownfield/processors/VariantTaskProvider.kt b/gradle-plugins/react/brownfield/src/main/kotlin/com/callstack/react/brownfield/processors/VariantTaskProvider.kt index d2aa07e6..f71364b9 100644 --- a/gradle-plugins/react/brownfield/src/main/kotlin/com/callstack/react/brownfield/processors/VariantTaskProvider.kt +++ b/gradle-plugins/react/brownfield/src/main/kotlin/com/callstack/react/brownfield/processors/VariantTaskProvider.kt @@ -95,9 +95,18 @@ class VariantTaskProvider(val project: Project) { preBuildTask.dependsOn("${appProject.path}:createBundle${capitalizedBundledAssetsVariantName}JsAndAssets") if (Utils.isExpoProject(project)) { - preBuildTask.dependsOn( - "${appProject.path}:create${capitalizedBundledAssetsVariantName}UpdatesResources", - ) + val updatesResourcesTaskName = + "create${capitalizedBundledAssetsVariantName}UpdatesResources" + + appProject.tasks.findByName(updatesResourcesTaskName)?.let { updatesTask -> + preBuildTask.dependsOn(updatesTask) + } + + appProject.tasks.whenTaskAdded { task -> + if (task.name == updatesResourcesTaskName) { + preBuildTask.dependsOn(task) + } + } } } } From 44047d68df506925e9a01ae5dce018cb5494c4f8 Mon Sep 17 00:00:00 2001 From: artus9033 Date: Thu, 25 Jun 2026 22:29:51 +0200 Subject: [PATCH 2/5] chore: bump up BGP version --- apps/RNApp/android/build.gradle | 2 +- apps/scripts/prepare-android-build-gradle-for-ci.ts | 2 +- gradle-plugins/react/brownfield/gradle.properties | 2 +- .../src/expo-config-plugin/android/utils/constants.ts | 2 +- ...generate-brownfield-gradle-plugin-release-notes.test.ts | 7 ++++--- 5 files changed, 8 insertions(+), 7 deletions(-) diff --git a/apps/RNApp/android/build.gradle b/apps/RNApp/android/build.gradle index ea9713ad..ad16aece 100644 --- a/apps/RNApp/android/build.gradle +++ b/apps/RNApp/android/build.gradle @@ -16,7 +16,7 @@ buildscript { classpath("com.android.tools.build:gradle") classpath("com.facebook.react:react-native-gradle-plugin") classpath("org.jetbrains.kotlin:kotlin-gradle-plugin") - classpath("com.callstack.react:brownfield-gradle-plugin:2.0.0-alpha02-SNAPSHOT") + classpath("com.callstack.react:brownfield-gradle-plugin:2.0.0-alpha03-SNAPSHOT") } } diff --git a/apps/scripts/prepare-android-build-gradle-for-ci.ts b/apps/scripts/prepare-android-build-gradle-for-ci.ts index d4f0c043..cb03d8d8 100644 --- a/apps/scripts/prepare-android-build-gradle-for-ci.ts +++ b/apps/scripts/prepare-android-build-gradle-for-ci.ts @@ -9,7 +9,7 @@ if (!projectDirName) { const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); -const SNAPSHOT_VERSION = '2.0.0-alpha02-SNAPSHOT'; +const SNAPSHOT_VERSION = '2.0.0-alpha03-SNAPSHOT'; const targetPath = path.resolve( __dirname, '..', diff --git a/gradle-plugins/react/brownfield/gradle.properties b/gradle-plugins/react/brownfield/gradle.properties index 6241bc8c..62309f54 100644 --- a/gradle-plugins/react/brownfield/gradle.properties +++ b/gradle-plugins/react/brownfield/gradle.properties @@ -1,6 +1,6 @@ PROJECT_ID=com.callstack.react.brownfield ARTIFACT_ID=brownfield-gradle-plugin -VERSION=2.0.0-alpha02 +VERSION=2.0.0-alpha03 GROUP=com.callstack.react IMPLEMENTATION_CLASS=com.callstack.react.brownfield.plugin.RNBrownfieldPlugin diff --git a/packages/react-native-brownfield/src/expo-config-plugin/android/utils/constants.ts b/packages/react-native-brownfield/src/expo-config-plugin/android/utils/constants.ts index 1faf9b09..385e25a3 100644 --- a/packages/react-native-brownfield/src/expo-config-plugin/android/utils/constants.ts +++ b/packages/react-native-brownfield/src/expo-config-plugin/android/utils/constants.ts @@ -1,2 +1,2 @@ -export const BROWNFIELD_PLUGIN_VERSION = '2.0.0-alpha02'; +export const BROWNFIELD_PLUGIN_VERSION = '2.0.0-alpha03'; export const brownfieldGradlePluginDependency = `classpath("com.callstack.react:brownfield-gradle-plugin:${BROWNFIELD_PLUGIN_VERSION}")`; diff --git a/scripts/__tests__/generate-brownfield-gradle-plugin-release-notes.test.ts b/scripts/__tests__/generate-brownfield-gradle-plugin-release-notes.test.ts index de0e0746..9981fe46 100644 --- a/scripts/__tests__/generate-brownfield-gradle-plugin-release-notes.test.ts +++ b/scripts/__tests__/generate-brownfield-gradle-plugin-release-notes.test.ts @@ -25,14 +25,14 @@ test('supports prerelease versions when finding the previous plugin tag', () => const previousTag = findPreviousPluginTag( [ 'brownfield-gradle-plugin/v2.0.0-alpha01', - 'brownfield-gradle-plugin/v2.0.0-alpha02', + 'brownfield-gradle-plugin/v2.0.0-alpha03', 'brownfield-gradle-plugin/v2.0.0-beta01', 'brownfield-gradle-plugin/v2.0.0', ], '2.0.0-beta01' ); - assert.equal(previousTag, 'brownfield-gradle-plugin/v2.0.0-alpha02'); + assert.equal(previousTag, 'brownfield-gradle-plugin/v2.0.0-alpha03'); }); test('treats a stable release as newer than its prereleases', () => { @@ -58,7 +58,8 @@ test('renders scoped release notes grouped by commit category', () => { }, { hash: 'dd8b8a0', - subject: 'fix: broken support for custom `appProjectName` in Gradle Plugin (#275)', + subject: + 'fix: broken support for custom `appProjectName` in Gradle Plugin (#275)', }, { hash: '6ea8da9', From afb4dcbd7dfc4ef0ff01502ab8775dbe430d49e1 Mon Sep 17 00:00:00 2001 From: artus9033 Date: Thu, 25 Jun 2026 23:05:27 +0200 Subject: [PATCH 3/5] refactor: use lazy Gradle syntax --- .../react/brownfield/processors/VariantTaskProvider.kt | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/gradle-plugins/react/brownfield/src/main/kotlin/com/callstack/react/brownfield/processors/VariantTaskProvider.kt b/gradle-plugins/react/brownfield/src/main/kotlin/com/callstack/react/brownfield/processors/VariantTaskProvider.kt index f71364b9..3fa0eb9e 100644 --- a/gradle-plugins/react/brownfield/src/main/kotlin/com/callstack/react/brownfield/processors/VariantTaskProvider.kt +++ b/gradle-plugins/react/brownfield/src/main/kotlin/com/callstack/react/brownfield/processors/VariantTaskProvider.kt @@ -98,15 +98,11 @@ class VariantTaskProvider(val project: Project) { val updatesResourcesTaskName = "create${capitalizedBundledAssetsVariantName}UpdatesResources" - appProject.tasks.findByName(updatesResourcesTaskName)?.let { updatesTask -> + appProject.tasks + .matching { it.name == updatesResourcesTaskName } + .configureEach { updatesTask -> preBuildTask.dependsOn(updatesTask) } - - appProject.tasks.whenTaskAdded { task -> - if (task.name == updatesResourcesTaskName) { - preBuildTask.dependsOn(task) - } - } } } } From 9d94afbb0458999d69cb637b1b1da4540ee84aed Mon Sep 17 00:00:00 2001 From: artus9033 Date: Thu, 25 Jun 2026 23:12:15 +0200 Subject: [PATCH 4/5] fix(test): skip MacOS-specific tests on Ubuntu runners --- .../__tests__/strip-framework-binary.test.ts | 156 +++++++++--------- 1 file changed, 79 insertions(+), 77 deletions(-) diff --git a/packages/cli/src/brownfield/utils/__tests__/strip-framework-binary.test.ts b/packages/cli/src/brownfield/utils/__tests__/strip-framework-binary.test.ts index ec9d4178..aada964b 100644 --- a/packages/cli/src/brownfield/utils/__tests__/strip-framework-binary.test.ts +++ b/packages/cli/src/brownfield/utils/__tests__/strip-framework-binary.test.ts @@ -65,76 +65,91 @@ describe('stripFrameworkBinary', () => { ); }); - it('strips binary from ios-arm64 slice', () => { - const xcframeworkPath = createMockXcframework(tempDir, 'TestFramework', [ - 'ios-arm64', - ]); - const binaryPath = path.join( - xcframeworkPath, - 'ios-arm64', - 'TestFramework.framework', - 'TestFramework' - ); - const originalContent = fs.readFileSync(binaryPath, 'utf-8'); - - stripFrameworkBinary(xcframeworkPath); - - const newContent = fs.readFileSync(binaryPath); - expect(newContent.toString()).not.toBe(originalContent); - expect(fs.existsSync(binaryPath)).toBe(true); - expect(mockLoggerSuccess).toHaveBeenCalledWith( - 'TestFramework.xcframework is now interface-only' - ); - }); - - it('strips binary from simulator slice with fat binary', () => { - const xcframeworkPath = createMockXcframework(tempDir, 'TestFramework', [ - 'ios-arm64_x86_64-simulator', - ]); - const binaryPath = path.join( - xcframeworkPath, - 'ios-arm64_x86_64-simulator', - 'TestFramework.framework', - 'TestFramework' - ); - const originalContent = fs.readFileSync(binaryPath, 'utf-8'); - - stripFrameworkBinary(xcframeworkPath); - - const newContent = fs.readFileSync(binaryPath); - expect(newContent.toString()).not.toBe(originalContent); + describe.skipIf(process.platform !== 'darwin')('with Xcode toolchain', () => { + it('strips binary from ios-arm64 slice', () => { + const xcframeworkPath = createMockXcframework(tempDir, 'TestFramework', [ + 'ios-arm64', + ]); + const binaryPath = path.join( + xcframeworkPath, + 'ios-arm64', + 'TestFramework.framework', + 'TestFramework' + ); + const originalContent = fs.readFileSync(binaryPath, 'utf-8'); + + stripFrameworkBinary(xcframeworkPath); + + const newContent = fs.readFileSync(binaryPath); + expect(newContent.toString()).not.toBe(originalContent); + expect(fs.existsSync(binaryPath)).toBe(true); + expect(mockLoggerSuccess).toHaveBeenCalledWith( + 'TestFramework.xcframework is now interface-only' + ); + }); - const archInfo = execSync(`xcrun lipo -info "${binaryPath}"`, { - encoding: 'utf-8', + it('strips binary from simulator slice with fat binary', () => { + const xcframeworkPath = createMockXcframework(tempDir, 'TestFramework', [ + 'ios-arm64_x86_64-simulator', + ]); + const binaryPath = path.join( + xcframeworkPath, + 'ios-arm64_x86_64-simulator', + 'TestFramework.framework', + 'TestFramework' + ); + const originalContent = fs.readFileSync(binaryPath, 'utf-8'); + + stripFrameworkBinary(xcframeworkPath); + + const newContent = fs.readFileSync(binaryPath); + expect(newContent.toString()).not.toBe(originalContent); + + const archInfo = execSync(`xcrun lipo -info "${binaryPath}"`, { + encoding: 'utf-8', + }); + expect(archInfo).toContain('arm64'); + expect(archInfo).toContain('x86_64'); }); - expect(archInfo).toContain('arm64'); - expect(archInfo).toContain('x86_64'); - }); - it('handles multiple slices', () => { - const xcframeworkPath = createMockXcframework(tempDir, 'TestFramework', [ - 'ios-arm64', - 'ios-arm64_x86_64-simulator', - ]); + it('handles multiple slices', () => { + const xcframeworkPath = createMockXcframework(tempDir, 'TestFramework', [ + 'ios-arm64', + 'ios-arm64_x86_64-simulator', + ]); + + stripFrameworkBinary(xcframeworkPath); + + const deviceBinary = path.join( + xcframeworkPath, + 'ios-arm64', + 'TestFramework.framework', + 'TestFramework' + ); + const simBinary = path.join( + xcframeworkPath, + 'ios-arm64_x86_64-simulator', + 'TestFramework.framework', + 'TestFramework' + ); + + expect(fs.existsSync(deviceBinary)).toBe(true); + expect(fs.existsSync(simBinary)).toBe(true); + expect(mockLoggerSuccess).toHaveBeenCalledOnce(); + }); - stripFrameworkBinary(xcframeworkPath); + it('ignores non-ios directories', () => { + const xcframeworkPath = createMockXcframework(tempDir, 'TestFramework', [ + 'ios-arm64', + ]); + fs.mkdirSync(path.join(xcframeworkPath, 'macos-arm64'), { + recursive: true, + }); - const deviceBinary = path.join( - xcframeworkPath, - 'ios-arm64', - 'TestFramework.framework', - 'TestFramework' - ); - const simBinary = path.join( - xcframeworkPath, - 'ios-arm64_x86_64-simulator', - 'TestFramework.framework', - 'TestFramework' - ); + stripFrameworkBinary(xcframeworkPath); - expect(fs.existsSync(deviceBinary)).toBe(true); - expect(fs.existsSync(simBinary)).toBe(true); - expect(mockLoggerSuccess).toHaveBeenCalledOnce(); + expect(mockLoggerSuccess).toHaveBeenCalledOnce(); + }); }); it('warns and skips unknown slice types', () => { @@ -164,17 +179,4 @@ describe('stripFrameworkBinary', () => { expect.stringContaining('No binary found at') ); }); - - it('ignores non-ios directories', () => { - const xcframeworkPath = createMockXcframework(tempDir, 'TestFramework', [ - 'ios-arm64', - ]); - fs.mkdirSync(path.join(xcframeworkPath, 'macos-arm64'), { - recursive: true, - }); - - stripFrameworkBinary(xcframeworkPath); - - expect(mockLoggerSuccess).toHaveBeenCalledOnce(); - }); }); From af1c8fbade15681906bab389e12a9af51fa0ea36 Mon Sep 17 00:00:00 2001 From: artus9033 Date: Thu, 25 Jun 2026 23:13:02 +0200 Subject: [PATCH 5/5] ci: run MacOS-specific vitest tests in native iOS workflow --- .github/workflows/ci.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 72e09b95..c1cc8dc6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -123,6 +123,11 @@ jobs: CLANG_MODULE_CACHE_PATH="$RUNNER_TEMP/swift-cache/clang" \ swift test --scratch-path "$RUNNER_TEMP/swift-cache/swiftpm" + - name: Run brownfield tests including MacOS-only tests + run: | + cd packages/react-native-brownfield + yarn test + android-androidapp-expo: name: Android road test (AndroidApp - Expo ${{ matrix.version }}) runs-on: ubuntu-latest