Skip to content

Commit b5c1522

Browse files
coadometa-codesync[bot]
authored andcommitted
Fix builds to not probe Maven for 1000.0.0 versioned artifacts (#58425)
Summary: Fixes issue with probing Maven Central repo for artifacts with version `1000.0.0` which are not stored there. The reason is that it creates most of the traffic on Maven Mirror that is forwarded to Maven Central due to artifacts not being cached because they are not found. After this change: - Gradle retains its existing dependency substitutions and forced versions. - Local file-based Maven repositories may still provide 1000.0.0 artifacts. - CocoaPods continues falling back to building React Native from source. - SwiftPM retains its existing 1000.0.0 → nightly resolution behavior. - Published React Native and Hermes versions continue to be queried normally. ## Changelog: [GENERAL][FIXED] - Changed builds to not probe Maven for `1000.0.0` versioned artifacts. Pull Request resolved: #58425 Test Plan: Ran `pod install` and build the HelloWorld workspace: ``` [MavenProbe] SKIP https://repo.reactnative.dev/maven2/.../1000.0.0/...reactnative-dependencies-debug.tar.gz [MavenProbe] SKIP https://repo1.maven.org/maven2/.../1000.0.0/...reactnative-dependencies-debug.tar.gz [ReactNativeDependencies] No prebuilt artifacts found, reverting to building from source. [MavenProbe] SKIP https://repo.reactnative.dev/maven2/.../1000.0.0/...reactnative-core-debug.tar.gz [MavenProbe] SKIP https://repo1.maven.org/maven2/.../1000.0.0/...reactnative-core-debug.tar.gz [ReactNativeCore] No prebuilt artifacts found, reverting to building from source. Pod installation complete! There are 85 dependencies from the Podfile and 84 total pods installed. ``` Ran SwiftPM setup on a disposable copy of HelloWorld and built the generated Xcode project. ``` [setup-apple-spm] React Native version: 1000.0.0 [download-spm-artifacts] Detected dev version (1000.0.0), resolving as nightly... [download-spm-artifacts] Resolved nightly: 0.89.0-nightly-20260909-b830082cf [MavenProbe] HEAD https://repo.reactnative.dev/maven2/.../0.89.0-nightly-20260909-b830082cf/...reactnative-core-debug.tar.gz [MavenProbe] HEAD https://repo.reactnative.dev/maven2/.../0.89.0-nightly-20260909-b830082cf/...reactnative-dependencies-debug.tar.gz ``` Published `react-android:1000.0.0` to `/tmp/maven-local`, then ran the repository’s real HelloWorld Android build and a verbose Gradle rebuild. ``` [MavenProbe] SKIP remote Maven https://repo.reactnative.dev/maven2 for React Native 1000.0.0 [MavenProbe] SKIP remote Maven https://repo.maven.apache.org/maven2/ for React Native 1000.0.0 ``` During the HelloWorld build: ``` [MavenProbe] SKIP remote Maven https://repo.maven.apache.org/maven2/ for React Native 1000.0.0 [MavenProbe] SKIP remote Maven https://dl.google.com/dl/android/maven2/ for React Native 1000.0.0 [MavenProbe] SKIP remote Maven https://www.jitpack.io for React Native 1000.0.0 BUILD SUCCESSFUL in 5s ``` Reviewed By: cipolleschi Differential Revision: D119646843 Pulled By: coado fbshipit-source-id: 66d1d36e8f00d9cb8beaac76aae720c1cbb079db
1 parent dcdb52b commit b5c1522

14 files changed

Lines changed: 311 additions & 2 deletions

File tree

packages/gradle-plugin/react-native-gradle-plugin/src/main/kotlin/com/facebook/react/ReactPlugin.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ class ReactPlugin : Plugin<Project> {
9595
val versionAndGroupStrings =
9696
readVersionAndGroupStrings(project, propertiesFile, hermesVersionPropertiesFile)
9797
configureDependencies(project, versionAndGroupStrings)
98-
configureRepositories(project, versionAndGroupStrings.isNightly)
98+
configureRepositories(project, versionAndGroupStrings)
9999
}
100100

101101
configureReactNativeNdk(project, extension)

packages/gradle-plugin/react-native-gradle-plugin/src/main/kotlin/com/facebook/react/utils/DependencyUtils.kt

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ import org.gradle.api.artifacts.repositories.MavenArtifactRepository
3131
internal object DependencyUtils {
3232
private const val REACT_NATIVE_MAVEN_MIRROR_URL = "https://repo.reactnative.dev/maven2"
3333
private const val REACT_NATIVE_MAVEN_MIRROR_ENABLED_ENV = "RCT_REACT_NATIVE_MAVEN_MIRROR_ENABLED"
34+
private const val UNPUBLISHED_MAVEN_VERSION = "1000.0.0"
3435

3536
internal data class Coordinates(
3637
val versionString: String,
@@ -124,6 +125,41 @@ internal object DependencyUtils {
124125
}
125126
}
126127

128+
/**
129+
* Configures repositories without asking remote repositories for versions that are known to be
130+
* unpublished.
131+
*/
132+
fun configureRepositories(project: Project, coordinates: Coordinates) {
133+
configureRepositories(project, coordinates.isNightly)
134+
135+
project.rootProject.allprojects { eachProject ->
136+
eachProject.repositories.withType(MavenArtifactRepository::class.java).configureEach { repo ->
137+
if (repo.url.scheme != "file") {
138+
repo.content { content ->
139+
if (!coordinates.versionString.isMavenArtifactVersionPublished()) {
140+
setOf(DEFAULT_INTERNAL_REACT_PUBLISHING_GROUP, coordinates.reactGroupString)
141+
.forEach { group ->
142+
content.excludeVersion(group, "react-native", UNPUBLISHED_MAVEN_VERSION)
143+
content.excludeVersion(group, "react-android", UNPUBLISHED_MAVEN_VERSION)
144+
}
145+
}
146+
if (!coordinates.hermesVersionString.isMavenArtifactVersionPublished()) {
147+
setOf(
148+
DEFAULT_INTERNAL_REACT_PUBLISHING_GROUP,
149+
DEFAULT_INTERNAL_HERMES_PUBLISHING_GROUP,
150+
coordinates.hermesGroupString,
151+
)
152+
.forEach { group ->
153+
content.excludeVersion(group, "hermes-engine", UNPUBLISHED_MAVEN_VERSION)
154+
content.excludeVersion(group, "hermes-android", UNPUBLISHED_MAVEN_VERSION)
155+
}
156+
}
157+
}
158+
}
159+
}
160+
}
161+
}
162+
127163
/**
128164
* This method takes care of configuring the resolution strategy for both the app and all the 3rd
129165
* party libraries which are auto-linked. Specifically it takes care of:
@@ -303,6 +339,8 @@ internal object DependencyUtils {
303339

304340
internal fun String.isNightly(): Boolean = this.startsWith("0.0.0") || "-nightly-" in this
305341

342+
internal fun String.isMavenArtifactVersionPublished(): Boolean = this != UNPUBLISHED_MAVEN_VERSION
343+
306344
internal fun Project.exclusiveEnterpriseRepository() =
307345
when {
308346
hasProperty(SCOPED_EXCLUSIVE_ENTEPRISE_REPOSITORY) ->

packages/gradle-plugin/react-native-gradle-plugin/src/test/kotlin/com/facebook/react/utils/DependencyUtilsTest.kt

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,18 @@ import com.facebook.react.utils.DependencyUtils.configureDependencies
1212
import com.facebook.react.utils.DependencyUtils.configureRepositories
1313
import com.facebook.react.utils.DependencyUtils.exclusiveEnterpriseRepository
1414
import com.facebook.react.utils.DependencyUtils.getDependencySubstitutions
15+
import com.facebook.react.utils.DependencyUtils.isMavenArtifactVersionPublished
1516
import com.facebook.react.utils.DependencyUtils.isNightly
1617
import com.facebook.react.utils.DependencyUtils.isReactNativeMavenMirrorEnabled
1718
import com.facebook.react.utils.DependencyUtils.mavenRepoFromURI
1819
import com.facebook.react.utils.DependencyUtils.mavenRepoFromUrl
1920
import com.facebook.react.utils.DependencyUtils.readVersionAndGroupStrings
2021
import com.facebook.react.utils.DependencyUtils.shouldAddJitPack
22+
import com.sun.net.httpserver.HttpServer
23+
import java.net.InetAddress
24+
import java.net.InetSocketAddress
2125
import java.net.URI
26+
import java.util.concurrent.atomic.AtomicInteger
2227
import org.assertj.core.api.Assertions.assertThat
2328
import org.gradle.api.artifacts.repositories.MavenArtifactRepository
2429
import org.gradle.testfixtures.ProjectBuilder
@@ -78,6 +83,42 @@ class DependencyUtilsTest {
7883
.isNotNull()
7984
}
8085

86+
@Test
87+
fun configureRepositories_withUnpublishedVersion_doesNotQueryRemoteRepository() {
88+
val requests = AtomicInteger()
89+
val loopbackAddress = InetAddress.getLoopbackAddress()
90+
val server = HttpServer.create(InetSocketAddress(loopbackAddress, 0), 0)
91+
server.createContext("/") { exchange ->
92+
requests.incrementAndGet()
93+
exchange.sendResponseHeaders(404, -1)
94+
exchange.close()
95+
}
96+
server.start()
97+
98+
try {
99+
val project = createProject()
100+
project.extensions.extraProperties.set(
101+
"exclusiveEnterpriseRepository",
102+
"http://${loopbackAddress.hostAddress}:${server.address.port}",
103+
)
104+
configureRepositories(project, DependencyUtils.Coordinates("1000.0.0", "4.5.6"))
105+
(project.repositories.first() as MavenArtifactRepository).isAllowInsecureProtocol = true
106+
107+
val published = project.configurations.create("published")
108+
project.dependencies.add(published.name, "com.facebook.react:react-android:0.88.0")
109+
assertThat(runCatching { published.resolve() }.isFailure).isTrue()
110+
assertThat(requests.get()).isGreaterThan(0)
111+
112+
requests.set(0)
113+
val unpublished = project.configurations.create("unpublished")
114+
project.dependencies.add(unpublished.name, "com.facebook.react:react-android:1000.0.0")
115+
assertThat(runCatching { unpublished.resolve() }.isFailure).isTrue()
116+
assertThat(requests.get()).isZero()
117+
} finally {
118+
server.stop(0)
119+
}
120+
}
121+
81122
@Test
82123
fun configureRepositories_containsGoogleRepo() {
83124
val repositoryURI = URI.create("https://dl.google.com/dl/android/maven2/")
@@ -414,6 +455,32 @@ class DependencyUtilsTest {
414455
assertThat(project.configurations.first().resolutionStrategy.forcedModules.isEmpty()).isTrue()
415456
}
416457

458+
@Test
459+
fun configureDependencies_withUnpublishedVersion_preservesResolutionStrategy() {
460+
val project = createProject()
461+
462+
configureDependencies(project, DependencyUtils.Coordinates("1000.0.0", "4.5.6"))
463+
464+
val forcedModules = project.configurations.first().resolutionStrategy.forcedModules
465+
assertThat(
466+
forcedModules.any {
467+
it.toString() == "com.facebook.react:react-android:1000.0.0"
468+
},
469+
)
470+
.isTrue()
471+
assertThat(forcedModules.any { it.toString() == "com.facebook.hermes:hermes-android:4.5.6" })
472+
.isTrue()
473+
474+
val dependencySubstitutions =
475+
getDependencySubstitutions(DependencyUtils.Coordinates("1000.0.0", "4.5.6"))
476+
assertThat(
477+
dependencySubstitutions.any {
478+
it.second == "com.facebook.react:react-android:1000.0.0"
479+
},
480+
)
481+
.isTrue()
482+
}
483+
417484
@Test
418485
fun configureDependencies_withVersionString_appliesResolutionStrategy() {
419486
val project = createProject()
@@ -577,6 +644,12 @@ class DependencyUtilsTest {
577644
assertThat(hermesVersionString).isEqualTo("1000.0.0")
578645
}
579646

647+
@Test
648+
fun isMavenArtifactVersionPublished_withMainVersion_returnsFalse() {
649+
assertThat("1000.0.0".isMavenArtifactVersionPublished()).isFalse()
650+
assertThat("0.88.0".isMavenArtifactVersionPublished()).isTrue()
651+
}
652+
580653
@Test
581654
fun readVersionString_withNightlyVersionString_returnsSnapshotVersion() {
582655
val propertiesFile =

packages/react-native/scripts/cocoapods/__tests__/maven_mirror_flag-test.rb

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55

66
require "test/unit"
77
require_relative "../utils.rb"
8+
require_relative "../rncore.rb"
9+
require_relative "../rndependencies.rb"
810
require_relative "../../../sdks/hermes-engine/hermes-utils.rb"
911

1012
class MavenMirrorFlagTests < Test::Unit::TestCase
@@ -39,4 +41,31 @@ def test_mavenMirror_isDisabledWhenExplicitlySetToFalse
3941
assert_false(ReactNativePodsUtils.react_native_maven_mirror_enabled?)
4042
assert_false(react_native_maven_mirror_enabled?)
4143
end
44+
45+
def test_unpublishedVersion_skipsAllArtifactLookups
46+
assert_false(ReactNativePodsUtils.maven_artifact_version_published?('1000.0.0'))
47+
assert_false(ReactNativePodsUtils.artifact_exists?('https://repo.reactnative.dev/maven2/example/1000.0.0/example.tar.gz'))
48+
assert_false(ReactNativePodsUtils.artifact_exists?('https://central.sonatype.com/example/1000.0.0-SNAPSHOT/example.tar.gz'))
49+
assert_false(ReactNativeCoreUtils.release_artifact_exists('1000.0.0'))
50+
assert_false(ReactNativeCoreUtils.nightly_artifact_exists('1000.0.0'))
51+
assert_false(ReactNativeDependenciesUtils.release_artifact_exists('1000.0.0'))
52+
assert_false(ReactNativeDependenciesUtils.nightly_artifact_exists('1000.0.0'))
53+
assert_false(release_artifact_exists('1000.0.0'))
54+
assert_false(hermes_artifact_exists('https://repo.reactnative.dev/maven2/example/1000.0.0/example.tar.gz'))
55+
56+
assert_equal(
57+
ReactNativeCoreUtils.stable_tarball_urls('1000.0.0', :debug).first,
58+
ReactNativeCoreUtils.stable_tarball_url('1000.0.0', :debug),
59+
)
60+
assert_equal(
61+
ReactNativeDependenciesUtils.release_tarball_urls('1000.0.0', :debug).first,
62+
ReactNativeDependenciesUtils.release_tarball_url('1000.0.0', :debug),
63+
)
64+
assert_equal(release_tarball_urls('1000.0.0', :debug).first, release_tarball_url('1000.0.0', :debug))
65+
end
66+
67+
def test_releaseVersion_isPublished
68+
assert_true(ReactNativePodsUtils.maven_artifact_version_published?('0.88.0'))
69+
assert_true(ReactNativePodsUtils.maven_artifact_version_published?('1000.0.0-abcdef123'))
70+
end
4271
end

packages/react-native/scripts/cocoapods/rncore.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -367,6 +367,8 @@ def self.stable_tarball_urls(version, build_type, dsyms = false)
367367
end
368368

369369
def self.nightly_tarball_url(version, configuration, dsyms = false)
370+
return "" if !ReactNativePodsUtils.maven_artifact_version_published?(version)
371+
370372
artefact_coordinate = "react-native-artifacts"
371373
artefact_name = "reactnative-core-#{dsyms ? "dSYM-" : ""}#{configuration ? configuration : "debug"}.tar.gz"
372374
xml_url = "https://central.sonatype.com/repository/maven-snapshots/com/facebook/react/#{artefact_coordinate}/#{version}-SNAPSHOT/maven-metadata.xml"
@@ -469,6 +471,8 @@ def self.release_artifact_exists(version)
469471
end
470472

471473
def self.nightly_artifact_exists(version)
474+
return false if !ReactNativePodsUtils.maven_artifact_version_published?(version)
475+
472476
return artifact_exists(nightly_tarball_url(version, :debug).gsub("\\", ""))
473477
end
474478

packages/react-native/scripts/cocoapods/rndependencies.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,8 @@ def self.release_tarball_urls(version, build_type)
250250
end
251251

252252
def self.nightly_tarball_url(version, build_type)
253+
return "" if !ReactNativePodsUtils.maven_artifact_version_published?(version)
254+
253255
artifact_coordinate = "react-native-artifacts"
254256
artifact_name = "reactnative-dependencies-#{build_type.to_s}.tar.gz"
255257
xml_url = "https://central.sonatype.com/repository/maven-snapshots/com/facebook/react/#{artifact_coordinate}/#{version}-SNAPSHOT/maven-metadata.xml"
@@ -377,6 +379,8 @@ def self.release_artifact_exists(version)
377379
end
378380

379381
def self.nightly_artifact_exists(version)
382+
return false if !ReactNativePodsUtils.maven_artifact_version_published?(version)
383+
380384
return artifact_exists(nightly_tarball_url(version, :debug).gsub("\\", ""))
381385
end
382386

packages/react-native/scripts/cocoapods/utils.rb

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
class ReactNativePodsUtils
1616
MAVEN_CENTRAL_REPOSITORY = "https://repo1.maven.org/maven2"
1717
REACT_NATIVE_MAVEN_MIRROR_REPOSITORY = "https://repo.reactnative.dev/maven2"
18+
UNPUBLISHED_MAVEN_VERSION = "1000.0.0"
1819

1920
# Opt-in removal of the legacy TurboModule and component interop layers. Both are
2021
# off by default and will become the default in a future React Native release.
@@ -49,6 +50,11 @@ def self.react_native_maven_mirror_enabled?()
4950
value.downcase != "false" && value != "0"
5051
end
5152

53+
def self.maven_artifact_version_published?(version)
54+
# 1000.0.0 identifies a source checkout on main and is never published to Maven.
55+
return version != UNPUBLISHED_MAVEN_VERSION
56+
end
57+
5258
def self.warn_if_not_on_arm64
5359
if SysctlChecker.new().call_sysctl_arm64() == 1 && !Environment.new().ruby_platform().include?('arm64')
5460
Pod::UI.warn 'Do not use "pod install" from inside Rosetta2 (x86_64 emulation on arm64).'
@@ -833,6 +839,9 @@ def self.resolve_use_frameworks(spec, header_mappings_dir: nil, module_name: nil
833839
# (DNS failure, no route, ...) the probe is left uncached so that a
834840
# transient hiccup doesn't permanently mark the artifact as missing.
835841
def self.artifact_exists?(tarball_url)
842+
unpublished_version = Regexp.escape(UNPUBLISHED_MAVEN_VERSION)
843+
return false if tarball_url.match?(%r{/#{unpublished_version}(?:-SNAPSHOT)?/})
844+
836845
unless @@artifact_exists_cache.key?(tarball_url)
837846
# -L is used to follow redirects, useful for the nightlies
838847
# The url is wrapped in quotes to avoid escaping & and ?.
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
/**
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*
7+
* @format
8+
* @noflow
9+
*/
10+
11+
'use strict';
12+
13+
const {
14+
computeNightlyTarballURL,
15+
isMavenArtifactVersionPublished,
16+
} = require('../utils');
17+
18+
describe('isMavenArtifactVersionPublished', () => {
19+
it('rejects the unpublished main version', () => {
20+
expect(isMavenArtifactVersionPublished('1000.0.0')).toBe(false);
21+
});
22+
23+
it.each([
24+
'0.88.0',
25+
'0.89.0-nightly-20260909-abcdef123',
26+
'1000.0.0-abcdef123',
27+
])('accepts published artifact version %s', version => {
28+
expect(isMavenArtifactVersionPublished(version)).toBe(true);
29+
});
30+
});
31+
32+
describe('computeNightlyTarballURL', () => {
33+
it('does not query snapshot metadata for the unpublished main version', async () => {
34+
const originalFetch = globalThis.fetch;
35+
globalThis.fetch = jest.fn();
36+
37+
try {
38+
await expect(
39+
computeNightlyTarballURL(
40+
'1000.0.0',
41+
'Debug',
42+
'react',
43+
'react-native-artifacts',
44+
'reactnative-dependencies-debug.tar.gz',
45+
),
46+
).rejects.toThrow(/artifacts are not published/);
47+
expect(globalThis.fetch).not.toHaveBeenCalled();
48+
} finally {
49+
globalThis.fetch = originalFetch;
50+
}
51+
});
52+
});

packages/react-native/scripts/ios-prebuild/hermes.js

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,11 @@
88
* @format
99
*/
1010

11-
const {createLogger, getMavenRepositoryUrls} = require('./utils');
11+
const {
12+
createLogger,
13+
getMavenRepositoryUrls,
14+
isMavenArtifactVersionPublished,
15+
} = require('./utils');
1216
const {execSync} = require('node:child_process');
1317
const fs = require('node:fs');
1418
const path = require('node:path');
@@ -204,6 +208,10 @@ async function findExistingTarballUrl(
204208
version /*: string */,
205209
buildType /*: BuildFlavor */,
206210
) /*: Promise<?string> */ {
211+
if (!isMavenArtifactVersionPublished(version)) {
212+
return null;
213+
}
214+
207215
const candidates = getTarballUrls(version, buildType);
208216
for (const url of candidates) {
209217
if (await hermesArtifactExists(url)) {
@@ -341,6 +349,11 @@ async function downloadHermesTarball(
341349
const tmpFile = `${artifactsPath}/hermes-ios.download`;
342350
try {
343351
fs.mkdirSync(artifactsPath, {recursive: true});
352+
if (!isMavenArtifactVersionPublished(version)) {
353+
throw new Error(
354+
`Maven artifacts are not published for the development version ${version}`,
355+
);
356+
}
344357
hermesLog(`Downloading Hermes tarball from ${tarballUrl}`);
345358

346359
const response /*: Response */ = await fetch(tarballUrl);

0 commit comments

Comments
 (0)