From b435a77900a9c9336b6b0843f311a64803f48f98 Mon Sep 17 00:00:00 2001 From: Wes Johnson Date: Tue, 16 Dec 2025 08:21:38 -0500 Subject: [PATCH 1/2] add plugin integration spec just for android --- test/run-integration.sh | 15 +++-- test/test-config-plugin-android.js | 97 ++++++++++++++++++++++++++++++ 2 files changed, 106 insertions(+), 6 deletions(-) create mode 100644 test/test-config-plugin-android.js diff --git a/test/run-integration.sh b/test/run-integration.sh index 9f27eb1..10f62d6 100755 --- a/test/run-integration.sh +++ b/test/run-integration.sh @@ -2,12 +2,12 @@ set -e -function logMark () { - echo "" - echo "###################################################################" - echo "$1" - echo "###################################################################" - echo "" +function logMark() { + echo "" + echo "###################################################################" + echo "$1" + echo "###################################################################" + echo "" } logMark "Setting up test environment" @@ -24,5 +24,8 @@ node ../test/test-babel-plugin.js logMark "Running test-config-plugin.js" node ../test/test-config-plugin.js +logMark "Running test-config-plugin-android.js" +node ../test/test-config-plugin-android.js + logMark "Running test-autolinking.js" node ../test/test-autolinking.js diff --git a/test/test-config-plugin-android.js b/test/test-config-plugin-android.js new file mode 100644 index 0000000..3a6425a --- /dev/null +++ b/test/test-config-plugin-android.js @@ -0,0 +1,97 @@ +const fs = require("fs"); +const cp = require("child_process"); +const yaml = require("yaml"); + +const expectedRuntimeModule = ` +export const BuildFlags = { + bundleIdScopedFeature: true, + newFeature: true, + publishedFeatured: true, + secretFeature: true +}; +`; + +const expectedManifestTag = + ''; + +const expectedPlistFlagArray = ` + EXBuildFlags + + secretFeature + newFeature + +`; + +addBundleIdScopedFlag(); +installExpoConfigPlugin(); +runPrebuild(); +assertFlagsAllTrue(); +assertAndroidManifest(); + +function addBundleIdScopedFlag() { + const flagsYmlString = fs.readFileSync("flags.yml", { encoding: "utf-8" }); + const flagConfig = yaml.parse(flagsYmlString); + flagConfig.flags.bundleIdScopedFeature = { + value: false, + invertFor: { + bundleId: ["com.example.app"], + }, + }; + fs.writeFileSync("flags.yml", yaml.stringify(flagConfig)); +} + +function installExpoConfigPlugin() { + const expoConfig = JSON.parse(fs.readFileSync("app.json", "utf-8")); + expoConfig.expo.plugins.push("expo-build-flags"); + expoConfig.expo.ios.bundleIdentifier = "com.example.app"; + expoConfig.expo.android.package = "com.example.app"; + fs.writeFileSync("app.json", JSON.stringify(expoConfig, null, 2)); +} + +function runPrebuild() { + cp.execSync("./node_modules/.bin/expo prebuild -p android --clean", { + env: { + ...process.env, + CI: 1, + EXPO_BUILD_FLAGS: "secretFeature,newFeature", + }, + }); +} + +function assertFlagsAllTrue() { + const fileContents = fs.readFileSync("constants/buildFlags.ts", "utf8"); + if (fileContents.trim() !== expectedRuntimeModule.trim()) { + console.log( + "received:\n\n", + `>${fileContents.trim()}<`, + "\n\n", + "expected:\n\n", + `>${expectedRuntimeModule.trim()}<`, + "\n\n" + ); + + throw new Error( + "Expected runtime buildFlags.ts module to contain all flags as true" + ); + } + + console.log( + "Assertion passed: Runtime build flags enabled by config plugin!" + ); +} + +function assertAndroidManifest() { + const fileContents = fs.readFileSync( + "android/app/src/main/AndroidManifest.xml", + "utf8" + ); + if (!fileContents.includes(expectedManifestTag)) { + throw new Error( + "Expected AndroidManifest.xml to contain EXBuildFlags meta-data tag" + ); + } + + console.log( + "Assertion passed: AndroidManifest.xml updated by config plugin!" + ); +} From ed945d30103df7fe5230d5fd15092408c9d5cc00 Mon Sep 17 00:00:00 2001 From: Wes Johnson Date: Tue, 16 Dec 2025 09:13:52 -0500 Subject: [PATCH 2/2] update plugin to handle bundle update for android --- src/config-plugin/index.ts | 64 +++++++++++++++++++------ test/overrides/default-flags.yml | 4 ++ test/overrides/expected-api-override.ts | 1 + test/overrides/expected-cli-override.ts | 1 + test/run-integration.sh | 5 ++ test/test-config-plugin-android.js | 33 ++----------- test/test-config-plugin.js | 1 + 7 files changed, 64 insertions(+), 45 deletions(-) diff --git a/src/config-plugin/index.ts b/src/config-plugin/index.ts index 9800a63..86c314f 100644 --- a/src/config-plugin/index.ts +++ b/src/config-plugin/index.ts @@ -1,10 +1,12 @@ import { ConfigPlugin, createRunOncePlugin, + Mod, withAndroidManifest, withDangerousMod, withInfoPlist, } from "@expo/config-plugins"; +import { ExpoConfig } from "@expo/config-types"; import { generateOverrides, resolveFlagsToInvert } from "../api"; import pkg from "../../package.json"; import { withFlaggedAutolinking } from "./withFlaggedAutolinking"; @@ -49,25 +51,57 @@ const withAppleBuildFlags: ConfigPlugin<{ flags: string[] }> = ( }); }; -const withBundleFlags: ConfigPlugin<{ flags: string[] }> = (config, props) => { +type BundlePluginProps = { flags: string[] }; + +const createCrossPlatformMod = + ({ + config, + props, + }: { + config: ExpoConfig; + props: BundlePluginProps; + }): Mod => + async (modConfig) => { + const { flags } = props; + let flagsToEnable = new Set(flags); + const invertable = await resolveFlagsToInvert(config); + if (invertable.flagsToEnable.size > 0) { + flagsToEnable = mergeSets(flagsToEnable, invertable.flagsToEnable); + } + await generateOverrides({ + flagsToEnable, + flagsToDisable: invertable.flagsToDisable, + }); + return modConfig; + }; + +const withAndroidBundleBuildFlags: ConfigPlugin = ( + config, + props +) => { return withDangerousMod(config, [ - "ios", // not platform-specific, but need to specify - async (modConfig) => { - const { flags } = props; - let flagsToEnable = new Set(flags); - const invertable = await resolveFlagsToInvert(config); - if (invertable.flagsToEnable.size > 0) { - flagsToEnable = mergeSets(flagsToEnable, invertable.flagsToEnable); - } - await generateOverrides({ - flagsToEnable, - flagsToDisable: invertable.flagsToDisable, - }); - return modConfig; - }, + "android", + createCrossPlatformMod({ config, props }), ]); }; +const withAppleBundleBuildFlags: ConfigPlugin = ( + config, + props +) => { + return withDangerousMod(config, [ + "ios", + createCrossPlatformMod({ config, props }), + ]); +}; + +const withBundleFlags: ConfigPlugin<{ flags: string[] }> = (config, props) => { + return withAppleBundleBuildFlags( + withAndroidBundleBuildFlags(config, props), + props + ); +}; + const parseEnvFlags = () => { const envFlags = process.env.EXPO_BUILD_FLAGS; if (!envFlags) { diff --git a/test/overrides/default-flags.yml b/test/overrides/default-flags.yml index 76826f5..12ef6ee 100644 --- a/test/overrides/default-flags.yml +++ b/test/overrides/default-flags.yml @@ -1,5 +1,9 @@ mergePath: "constants/buildFlags.ts" flags: + secretAndroidFeature: + value: false + meta: + team: "platform" secretFeature: value: false meta: diff --git a/test/overrides/expected-api-override.ts b/test/overrides/expected-api-override.ts index 14bda89..447343e 100644 --- a/test/overrides/expected-api-override.ts +++ b/test/overrides/expected-api-override.ts @@ -1,5 +1,6 @@ export const BuildFlags = { newFeature: true, publishedFeatured: true, + secretAndroidFeature: false, secretFeature: false }; diff --git a/test/overrides/expected-cli-override.ts b/test/overrides/expected-cli-override.ts index a5b303a..78bd037 100644 --- a/test/overrides/expected-cli-override.ts +++ b/test/overrides/expected-cli-override.ts @@ -1,5 +1,6 @@ export const BuildFlags = { newFeature: false, publishedFeatured: true, + secretAndroidFeature: false, secretFeature: true }; diff --git a/test/run-integration.sh b/test/run-integration.sh index 10f62d6..b38fd79 100755 --- a/test/run-integration.sh +++ b/test/run-integration.sh @@ -1,5 +1,10 @@ #!/bin/bash +# +# WARNING - these tests build on top of each other +# adjust your initial test flag state accordingly +# + set -e function logMark() { diff --git a/test/test-config-plugin-android.js b/test/test-config-plugin-android.js index 3a6425a..af12d70 100644 --- a/test/test-config-plugin-android.js +++ b/test/test-config-plugin-android.js @@ -7,26 +7,15 @@ export const BuildFlags = { bundleIdScopedFeature: true, newFeature: true, publishedFeatured: true, - secretFeature: true + secretAndroidFeature: true, + secretFeature: false }; `; -const expectedManifestTag = - ''; - -const expectedPlistFlagArray = ` - EXBuildFlags - - secretFeature - newFeature - -`; - addBundleIdScopedFlag(); installExpoConfigPlugin(); runPrebuild(); assertFlagsAllTrue(); -assertAndroidManifest(); function addBundleIdScopedFlag() { const flagsYmlString = fs.readFileSync("flags.yml", { encoding: "utf-8" }); @@ -53,7 +42,7 @@ function runPrebuild() { env: { ...process.env, CI: 1, - EXPO_BUILD_FLAGS: "secretFeature,newFeature", + EXPO_BUILD_FLAGS: "secretAndroidFeature", }, }); } @@ -79,19 +68,3 @@ function assertFlagsAllTrue() { "Assertion passed: Runtime build flags enabled by config plugin!" ); } - -function assertAndroidManifest() { - const fileContents = fs.readFileSync( - "android/app/src/main/AndroidManifest.xml", - "utf8" - ); - if (!fileContents.includes(expectedManifestTag)) { - throw new Error( - "Expected AndroidManifest.xml to contain EXBuildFlags meta-data tag" - ); - } - - console.log( - "Assertion passed: AndroidManifest.xml updated by config plugin!" - ); -} diff --git a/test/test-config-plugin.js b/test/test-config-plugin.js index 42aeb68..8934c49 100644 --- a/test/test-config-plugin.js +++ b/test/test-config-plugin.js @@ -7,6 +7,7 @@ export const BuildFlags = { bundleIdScopedFeature: true, newFeature: true, publishedFeatured: true, + secretAndroidFeature: false, secretFeature: true }; `;