From 2b4cb4e901ad8459793cf02939f4317a7dd9dea5 Mon Sep 17 00:00:00 2001 From: Zeya Peng Date: Wed, 8 Jul 2026 10:10:52 -0700 Subject: [PATCH] Fix release e2e local testing on macOS / npm >= 11 (prerelease --tag + gradle.properties) (#57469) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Summary: Two fixes that make `test-release-local` work again for release-candidate testing on macOS with modern npm. Both are in the local release-testing scripts only (no product code). ### 1. `npm publish` needs `--tag` for prereleases (npm >= 11) Publishing the monorepo packages to the local Verdaccio proxy fails on npm >= 11 for a prerelease version (e.g. `0.87.0-rc.0`): ``` react-native/asset-utils (packages/asset-utils) ........................ Failed ❌ npm error You must specify a tag using --tag when publishing a prerelease version. ``` npm now refuses to publish a prerelease to the default `latest` dist-tag without an explicit `--tag`. Fix: when the version is a prerelease (contains `-`), pass `--tag prerelease`. Stable versions are unaffected. (`scripts/e2e/init-project-e2e.js`) ### 2. `echo -e` corrupts `gradle.properties` on macOS Setting up the Android test project, the script appends the maven-local repo path with `echo -e`. On macOS, `/bin/sh`'s `echo` doesn't support `-e`, so it writes a literal `-e ` onto the previous property, corrupting the file: ``` Cannot parse project property android.newDsl='false-e ' of type 'class java.lang.String' as boolean. Expected 'true' or 'false'. ``` Fix: use `printf`, which is portable. (`scripts/release-testing/test-release-local.js`) ## Changelog [INTERNAL] - Fix `test-release-local` for prerelease testing on macOS / npm >= 11 Test Plan: On npm 11.4.2 / Node 24, `yarn test-release-local -t RNTestProject -p Android -c ` against `0.87.0-rc.0`: - **Before:** failed at "Publishing packages to local npm proxy" (prerelease `--tag` error); after working around that, failed at `app:installDebug` with the `android.newDsl='false-e '` parse error. - **After:** packages publish to the local proxy with `--tag prerelease`, `gradle.properties` gets a well-formed `react.internal.mavenLocalRepo` line, and the app builds/installs against the RC's maven-local artifacts. - iOS (`-t RNTestProject -p iOS`) also gets past the publish step with fix https://github.com/react/react-native/issues/1. - Stable (non-prerelease) versions: `publishTag` is empty and `printf` behaves identically, so no behavior change. Reviewed By: christophpurrer Differential Revision: D110925222 Pulled By: zeyap --- scripts/e2e/init-project-e2e.js | 6 +++++- scripts/release-testing/test-release-local.js | 6 ++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/scripts/e2e/init-project-e2e.js b/scripts/e2e/init-project-e2e.js index 30db3b9c33d2..d8869d1ce0f7 100644 --- a/scripts/e2e/init-project-e2e.js +++ b/scripts/e2e/init-project-e2e.js @@ -112,6 +112,10 @@ async function initNewProjectFromSource( // packages are updated in a lockstep, let's get the version of the first one const version = packages[Object.keys(packages)[0]].packageJson.version; + // npm >= 11 refuses to publish a prerelease version (e.g. 0.87.0-rc.0) + // without an explicit --tag, since it would otherwise become `latest`. + const publishTag = version.includes('-') ? ' --tag prerelease' : ''; + console.log('Publishing packages to local npm proxy\n'); for (const {path: packagePath, packageJson} of Object.values(packages)) { const desc = `${packageJson.name} (${path.relative( @@ -122,7 +126,7 @@ async function initNewProjectFromSource( `${desc} ${styleText('dim', '.').repeat(Math.max(0, 72 - desc.length))} `, ); execSync( - `npm publish --registry ${VERDACCIO_SERVER_URL} --access public`, + `npm publish --registry ${VERDACCIO_SERVER_URL} --access public${publishTag}`, { cwd: packagePath, stdio: verbose ? 'inherit' : [process.stderr], diff --git a/scripts/release-testing/test-release-local.js b/scripts/release-testing/test-release-local.js index 31a0733af47b..c5593709e93e 100644 --- a/scripts/release-testing/test-release-local.js +++ b/scripts/release-testing/test-release-local.js @@ -290,9 +290,11 @@ async function testRNTestProject( cd('RNTestProject'); - // need to do this here so that Android will be properly setup either way + // need to do this here so that Android will be properly setup either way. + // Use printf, not `echo -e`: on macOS /bin/sh's echo doesn't support -e, so + // it appends a literal "-e " onto the previous property and corrupts the file. exec( - `echo -e "\nreact.internal.mavenLocalRepo=${mavenLocalPath}" >> android/gradle.properties`, + `printf '\\nreact.internal.mavenLocalRepo=%s\\n' "${mavenLocalPath}" >> android/gradle.properties`, ); // Only build the simulator architecture. CI is however generating only that one.