Skip to content

Commit 8f83106

Browse files
committed
fix probing maven central for artifacts with 1000.0.0 version
1 parent a385cec commit 8f83106

13 files changed

Lines changed: 217 additions & 8 deletions

File tree

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

Lines changed: 22 additions & 5 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,
@@ -135,6 +136,11 @@ internal object DependencyUtils {
135136
coordinates: Coordinates,
136137
) {
137138
if (coordinates.versionString.isBlank() || coordinates.hermesVersionString.isBlank()) return
139+
140+
val shouldConfigureReact = coordinates.versionString.isMavenArtifactVersionPublished()
141+
val shouldConfigureHermes = coordinates.hermesVersionString.isMavenArtifactVersionPublished()
142+
if (!shouldConfigureReact && !shouldConfigureHermes) return
143+
138144
project.rootProject.allprojects { eachProject ->
139145
eachProject.configurations.all { configuration ->
140146
// Here we set a dependencySubstitution for both react-native and hermes-engine as those
@@ -146,10 +152,15 @@ internal object DependencyUtils {
146152
it.substitute(it.module(module)).using(it.module(dest)).because(reason)
147153
}
148154
}
149-
configuration.resolutionStrategy.force(
150-
"${coordinates.reactGroupString}:react-android:${coordinates.versionString}",
151-
)
152-
if (!(eachProject.findProperty(INTERNAL_USE_HERMES_NIGHTLY) as? String).toBoolean()) {
155+
if (shouldConfigureReact) {
156+
configuration.resolutionStrategy.force(
157+
"${coordinates.reactGroupString}:react-android:${coordinates.versionString}",
158+
)
159+
}
160+
if (
161+
shouldConfigureHermes &&
162+
!(eachProject.findProperty(INTERNAL_USE_HERMES_NIGHTLY) as? String).toBoolean()
163+
) {
153164
// Contributors only: The hermes-engine version is forced only if the user has
154165
// not opted into using nightlies for local development.
155166
configuration.resolutionStrategy.force(
@@ -212,7 +223,10 @@ internal object DependencyUtils {
212223
),
213224
)
214225
}
215-
return dependencySubstitution
226+
// 1000.0.0 identifies a source checkout on main and is never published to Maven.
227+
return dependencySubstitution.filterNot { (_, destination, _) ->
228+
!destination.substringAfterLast(':').isMavenArtifactVersionPublished()
229+
}
216230
}
217231

218232
fun readVersionAndGroupStrings(
@@ -303,6 +317,9 @@ internal object DependencyUtils {
303317

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

320+
internal fun String.isMavenArtifactVersionPublished(): Boolean =
321+
isNotBlank() && this != UNPUBLISHED_MAVEN_VERSION
322+
306323
internal fun Project.exclusiveEnterpriseRepository() =
307324
when {
308325
hasProperty(SCOPED_EXCLUSIVE_ENTEPRISE_REPOSITORY) ->

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

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ 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
@@ -414,6 +415,22 @@ class DependencyUtilsTest {
414415
assertThat(project.configurations.first().resolutionStrategy.forcedModules.isEmpty()).isTrue()
415416
}
416417

418+
@Test
419+
fun configureDependencies_withUnpublishedVersion_doesNotRequestReactNativeArtifacts() {
420+
val project = createProject()
421+
422+
configureDependencies(project, DependencyUtils.Coordinates("1000.0.0", "4.5.6"))
423+
424+
val forcedModules = project.configurations.first().resolutionStrategy.forcedModules
425+
assertThat(forcedModules.none { it.toString().contains(":1000.0.0") }).isTrue()
426+
assertThat(forcedModules.any { it.toString() == "com.facebook.hermes:hermes-android:4.5.6" })
427+
.isTrue()
428+
429+
val dependencySubstitutions =
430+
getDependencySubstitutions(DependencyUtils.Coordinates("1000.0.0", "4.5.6"))
431+
assertThat(dependencySubstitutions.none { it.second.contains(":1000.0.0") }).isTrue()
432+
}
433+
417434
@Test
418435
fun configureDependencies_withVersionString_appliesResolutionStrategy() {
419436
val project = createProject()
@@ -577,6 +594,12 @@ class DependencyUtilsTest {
577594
assertThat(hermesVersionString).isEqualTo("1000.0.0")
578595
}
579596

597+
@Test
598+
fun isMavenArtifactVersionPublished_withMainVersion_returnsFalse() {
599+
assertThat("1000.0.0".isMavenArtifactVersionPublished()).isFalse()
600+
assertThat("0.88.0".isMavenArtifactVersionPublished()).isTrue()
601+
}
602+
580603
@Test
581604
fun readVersionString_withNightlyVersionString_returnsSnapshotVersion() {
582605
val propertiesFile =

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

Lines changed: 18 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,20 @@ 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(ReactNativeCoreUtils.release_artifact_exists('1000.0.0'))
49+
assert_false(ReactNativeCoreUtils.nightly_artifact_exists('1000.0.0'))
50+
assert_false(ReactNativeDependenciesUtils.release_artifact_exists('1000.0.0'))
51+
assert_false(ReactNativeDependenciesUtils.nightly_artifact_exists('1000.0.0'))
52+
assert_false(release_artifact_exists('1000.0.0'))
53+
assert_false(hermes_artifact_exists('https://repo.reactnative.dev/maven2/example/1000.0.0/example.tar.gz'))
54+
end
55+
56+
def test_releaseVersion_isPublished
57+
assert_true(ReactNativePodsUtils.maven_artifact_version_published?('0.88.0'))
58+
assert_true(ReactNativePodsUtils.maven_artifact_version_published?('1000.0.0-abcdef123'))
59+
end
4260
end

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -350,6 +350,8 @@ def self.generate_plist_content(mappings)
350350
end
351351

352352
def self.stable_tarball_url(version, build_type, dsyms = false)
353+
return nil if !ReactNativePodsUtils.maven_artifact_version_published?(version)
354+
353355
candidates = stable_tarball_urls(version, build_type, dsyms)
354356
return candidates.find { |url| artifact_exists(url) } || candidates.first
355357
end
@@ -367,6 +369,8 @@ def self.stable_tarball_urls(version, build_type, dsyms = false)
367369
end
368370

369371
def self.nightly_tarball_url(version, configuration, dsyms = false)
372+
return "" if !ReactNativePodsUtils.maven_artifact_version_published?(version)
373+
370374
artefact_coordinate = "react-native-artifacts"
371375
artefact_name = "reactnative-core-#{dsyms ? "dSYM-" : ""}#{configuration ? configuration : "debug"}.tar.gz"
372376
xml_url = "https://central.sonatype.com/repository/maven-snapshots/com/facebook/react/#{artefact_coordinate}/#{version}-SNAPSHOT/maven-metadata.xml"
@@ -465,10 +469,14 @@ def self.download_rncore_tarball(react_native_path, tarball_url, version, config
465469
end
466470

467471
def self.release_artifact_exists(version)
472+
return false if !ReactNativePodsUtils.maven_artifact_version_published?(version)
473+
468474
return stable_tarball_urls(version, :debug).any? { |url| artifact_exists(url) }
469475
end
470476

471477
def self.nightly_artifact_exists(version)
478+
return false if !ReactNativePodsUtils.maven_artifact_version_published?(version)
479+
472480
return artifact_exists(nightly_tarball_url(version, :debug).gsub("\\", ""))
473481
end
474482

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,8 @@ def self.podspec_source_download_prebuild_release_tarball()
232232
end
233233

234234
def self.release_tarball_url(version, build_type)
235+
return nil if !ReactNativePodsUtils.maven_artifact_version_published?(version)
236+
235237
candidates = release_tarball_urls(version, build_type)
236238
return candidates.find { |url| artifact_exists(url) } || candidates.first
237239
end
@@ -250,6 +252,8 @@ def self.release_tarball_urls(version, build_type)
250252
end
251253

252254
def self.nightly_tarball_url(version, build_type)
255+
return "" if !ReactNativePodsUtils.maven_artifact_version_published?(version)
256+
253257
artifact_coordinate = "react-native-artifacts"
254258
artifact_name = "reactnative-dependencies-#{build_type.to_s}.tar.gz"
255259
xml_url = "https://central.sonatype.com/repository/maven-snapshots/com/facebook/react/#{artifact_coordinate}/#{version}-SNAPSHOT/maven-metadata.xml"
@@ -373,10 +377,14 @@ def self.download_rndeps_tarball(react_native_path, tarball_url, version, config
373377
end
374378

375379
def self.release_artifact_exists(version)
380+
return false if !ReactNativePodsUtils.maven_artifact_version_published?(version)
381+
376382
return release_tarball_urls(version, :debug).any? { |url| artifact_exists(url) }
377383
end
378384

379385
def self.nightly_artifact_exists(version)
386+
return false if !ReactNativePodsUtils.maven_artifact_version_published?(version)
387+
380388
return artifact_exists(nightly_tarball_url(version, :debug).gsub("\\", ""))
381389
end
382390

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

Lines changed: 8 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,8 @@ 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+
return false if tarball_url.include?("/#{UNPUBLISHED_MAVEN_VERSION}/")
843+
836844
unless @@artifact_exists_cache.key?(tarball_url)
837845
# -L is used to follow redirects, useful for the nightlies
838846
# 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: 9 additions & 2 deletions
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');
@@ -62,7 +66,10 @@ async function prepareHermesArtifactsAsync(
6266
// Resolve the version from the environment variable or use the default version
6367
let resolvedVersion = process.env.HERMES_VERSION ?? 'latest-v1';
6468

65-
if (resolvedVersion === 'latest-v1') {
69+
if (
70+
resolvedVersion === 'latest-v1' ||
71+
!isMavenArtifactVersionPublished(resolvedVersion)
72+
) {
6673
// TODO: rename 'latest-v1' to 'latest' once V1 is the only Hermes on npm
6774
hermesLog('Using latest-v1 tarball');
6875
const hermesVersion = await getLatestHermesVersionFromNPM();

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

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ const {
1414
computeNightlyTarballURL,
1515
createLogger,
1616
getMavenRepositoryUrls,
17+
isMavenArtifactVersionPublished,
1718
} = require('./utils');
1819
const {execSync} = require('node:child_process');
1920
const fs = require('node:fs');
@@ -49,7 +50,10 @@ async function prepareReactNativeDependenciesArtifactsAsync(
4950
// Resolve the version from the environment variable or use the default version
5051
let resolvedVersion = process.env.RN_DEP_VERSION ?? version;
5152

52-
if (resolvedVersion === 'nightly') {
53+
if (
54+
resolvedVersion === 'nightly' ||
55+
!isMavenArtifactVersionPublished(resolvedVersion)
56+
) {
5357
dependencyLog('Using latest nightly tarball');
5458
const rnVersion = await getNightlyVersionFromNPM();
5559
resolvedVersion = rnVersion;

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

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ const path = require('node:path');
1717
const MAVEN_CENTRAL_REPOSITORY = 'https://repo1.maven.org/maven2';
1818
const REACT_NATIVE_MAVEN_MIRROR_REPOSITORY =
1919
'https://repo.reactnative.dev/maven2';
20+
const UNPUBLISHED_MAVEN_VERSION = '1000.0.0';
2021

2122
/**
2223
* Creates a folder if it does not exist
@@ -96,6 +97,12 @@ async function computeNightlyTarballURL(
9697
artifactCoordinate /*: string */,
9798
artifactName /*: string */,
9899
) /*: Promise<string> */ {
100+
if (!isMavenArtifactVersionPublished(version)) {
101+
throw new Error(
102+
`Maven artifacts are not published for the development version ${version}`,
103+
);
104+
}
105+
99106
const xmlUrl = `https://central.sonatype.com/repository/maven-snapshots/com/facebook/${subGroup}/${artifactCoordinate}/${version}-SNAPSHOT/maven-metadata.xml`;
100107

101108
const response = await fetch(xmlUrl);
@@ -156,11 +163,17 @@ function isReactNativeMavenMirrorEnabled() /*: boolean */ {
156163
return value.toLowerCase() !== 'false' && value !== '0';
157164
}
158165

166+
function isMavenArtifactVersionPublished(version /*: string */) /*: boolean */ {
167+
// 1000.0.0 identifies a source checkout on main and is never published to Maven.
168+
return version !== UNPUBLISHED_MAVEN_VERSION;
169+
}
170+
159171
module.exports = {
160172
createFolderIfNotExists,
161173
findFirst,
162174
throwIfOnEden,
163175
createLogger,
164176
computeNightlyTarballURL,
165177
getMavenRepositoryUrls,
178+
isMavenArtifactVersionPublished,
166179
};

0 commit comments

Comments
 (0)