Skip to content

Commit 6e1169c

Browse files
committed
fixes
1 parent 8f83106 commit 6e1169c

12 files changed

Lines changed: 157 additions & 58 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: 41 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,41 @@ internal object DependencyUtils {
125125
}
126126
}
127127

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+
128163
/**
129164
* This method takes care of configuring the resolution strategy for both the app and all the 3rd
130165
* party libraries which are auto-linked. Specifically it takes care of:
@@ -136,11 +171,6 @@ internal object DependencyUtils {
136171
coordinates: Coordinates,
137172
) {
138173
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-
144174
project.rootProject.allprojects { eachProject ->
145175
eachProject.configurations.all { configuration ->
146176
// Here we set a dependencySubstitution for both react-native and hermes-engine as those
@@ -152,15 +182,10 @@ internal object DependencyUtils {
152182
it.substitute(it.module(module)).using(it.module(dest)).because(reason)
153183
}
154184
}
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-
) {
185+
configuration.resolutionStrategy.force(
186+
"${coordinates.reactGroupString}:react-android:${coordinates.versionString}",
187+
)
188+
if (!(eachProject.findProperty(INTERNAL_USE_HERMES_NIGHTLY) as? String).toBoolean()) {
164189
// Contributors only: The hermes-engine version is forced only if the user has
165190
// not opted into using nightlies for local development.
166191
configuration.resolutionStrategy.force(
@@ -223,10 +248,7 @@ internal object DependencyUtils {
223248
),
224249
)
225250
}
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-
}
251+
return dependencySubstitution
230252
}
231253

232254
fun readVersionAndGroupStrings(
@@ -318,7 +340,7 @@ internal object DependencyUtils {
318340
internal fun String.isNightly(): Boolean = this.startsWith("0.0.0") || "-nightly-" in this
319341

320342
internal fun String.isMavenArtifactVersionPublished(): Boolean =
321-
isNotBlank() && this != UNPUBLISHED_MAVEN_VERSION
343+
this != UNPUBLISHED_MAVEN_VERSION
322344

323345
internal fun Project.exclusiveEnterpriseRepository() =
324346
when {

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

Lines changed: 51 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,10 @@ import com.facebook.react.utils.DependencyUtils.mavenRepoFromURI
1919
import com.facebook.react.utils.DependencyUtils.mavenRepoFromUrl
2020
import com.facebook.react.utils.DependencyUtils.readVersionAndGroupStrings
2121
import com.facebook.react.utils.DependencyUtils.shouldAddJitPack
22+
import com.sun.net.httpserver.HttpServer
23+
import java.net.InetSocketAddress
2224
import java.net.URI
25+
import java.util.concurrent.atomic.AtomicInteger
2326
import org.assertj.core.api.Assertions.assertThat
2427
import org.gradle.api.artifacts.repositories.MavenArtifactRepository
2528
import org.gradle.testfixtures.ProjectBuilder
@@ -79,6 +82,41 @@ class DependencyUtilsTest {
7982
.isNotNull()
8083
}
8184

85+
@Test
86+
fun configureRepositories_withUnpublishedVersion_doesNotQueryRemoteRepository() {
87+
val requests = AtomicInteger()
88+
val server = HttpServer.create(InetSocketAddress("127.0.0.1", 0), 0)
89+
server.createContext("/") { exchange ->
90+
requests.incrementAndGet()
91+
exchange.sendResponseHeaders(404, -1)
92+
exchange.close()
93+
}
94+
server.start()
95+
96+
try {
97+
val project = createProject()
98+
project.extensions.extraProperties.set(
99+
"exclusiveEnterpriseRepository",
100+
"http://127.0.0.1:${server.address.port}",
101+
)
102+
configureRepositories(project, DependencyUtils.Coordinates("1000.0.0", "4.5.6"))
103+
(project.repositories.first() as MavenArtifactRepository).isAllowInsecureProtocol = true
104+
105+
val published = project.configurations.create("published")
106+
project.dependencies.add(published.name, "com.facebook.react:react-android:0.88.0")
107+
assertThat(runCatching { published.resolve() }.isFailure).isTrue()
108+
assertThat(requests.get()).isGreaterThan(0)
109+
110+
requests.set(0)
111+
val unpublished = project.configurations.create("unpublished")
112+
project.dependencies.add(unpublished.name, "com.facebook.react:react-android:1000.0.0")
113+
assertThat(runCatching { unpublished.resolve() }.isFailure).isTrue()
114+
assertThat(requests.get()).isZero()
115+
} finally {
116+
server.stop(0)
117+
}
118+
}
119+
82120
@Test
83121
fun configureRepositories_containsGoogleRepo() {
84122
val repositoryURI = URI.create("https://dl.google.com/dl/android/maven2/")
@@ -416,19 +454,29 @@ class DependencyUtilsTest {
416454
}
417455

418456
@Test
419-
fun configureDependencies_withUnpublishedVersion_doesNotRequestReactNativeArtifacts() {
457+
fun configureDependencies_withUnpublishedVersion_preservesResolutionStrategy() {
420458
val project = createProject()
421459

422460
configureDependencies(project, DependencyUtils.Coordinates("1000.0.0", "4.5.6"))
423461

424462
val forcedModules = project.configurations.first().resolutionStrategy.forcedModules
425-
assertThat(forcedModules.none { it.toString().contains(":1000.0.0") }).isTrue()
463+
assertThat(
464+
forcedModules.any {
465+
it.toString() == "com.facebook.react:react-android:1000.0.0"
466+
},
467+
)
468+
.isTrue()
426469
assertThat(forcedModules.any { it.toString() == "com.facebook.hermes:hermes-android:4.5.6" })
427470
.isTrue()
428471

429472
val dependencySubstitutions =
430473
getDependencySubstitutions(DependencyUtils.Coordinates("1000.0.0", "4.5.6"))
431-
assertThat(dependencySubstitutions.none { it.second.contains(":1000.0.0") }).isTrue()
474+
assertThat(
475+
dependencySubstitutions.any {
476+
it.second == "com.facebook.react:react-android:1000.0.0"
477+
},
478+
)
479+
.isTrue()
432480
}
433481

434482
@Test

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

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,12 +45,23 @@ def test_mavenMirror_isDisabledWhenExplicitlySetToFalse
4545
def test_unpublishedVersion_skipsAllArtifactLookups
4646
assert_false(ReactNativePodsUtils.maven_artifact_version_published?('1000.0.0'))
4747
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'))
4849
assert_false(ReactNativeCoreUtils.release_artifact_exists('1000.0.0'))
4950
assert_false(ReactNativeCoreUtils.nightly_artifact_exists('1000.0.0'))
5051
assert_false(ReactNativeDependenciesUtils.release_artifact_exists('1000.0.0'))
5152
assert_false(ReactNativeDependenciesUtils.nightly_artifact_exists('1000.0.0'))
5253
assert_false(release_artifact_exists('1000.0.0'))
5354
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))
5465
end
5566

5667
def test_releaseVersion_isPublished

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

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -350,8 +350,6 @@ 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-
355353
candidates = stable_tarball_urls(version, build_type, dsyms)
356354
return candidates.find { |url| artifact_exists(url) } || candidates.first
357355
end
@@ -469,8 +467,6 @@ def self.download_rncore_tarball(react_native_path, tarball_url, version, config
469467
end
470468

471469
def self.release_artifact_exists(version)
472-
return false if !ReactNativePodsUtils.maven_artifact_version_published?(version)
473-
474470
return stable_tarball_urls(version, :debug).any? { |url| artifact_exists(url) }
475471
end
476472

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

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -232,8 +232,6 @@ 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-
237235
candidates = release_tarball_urls(version, build_type)
238236
return candidates.find { |url| artifact_exists(url) } || candidates.first
239237
end
@@ -377,8 +375,6 @@ def self.download_rndeps_tarball(react_native_path, tarball_url, version, config
377375
end
378376

379377
def self.release_artifact_exists(version)
380-
return false if !ReactNativePodsUtils.maven_artifact_version_published?(version)
381-
382378
return release_tarball_urls(version, :debug).any? { |url| artifact_exists(url) }
383379
end
384380

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -839,7 +839,8 @@ def self.resolve_use_frameworks(spec, header_mappings_dir: nil, module_name: nil
839839
# (DNS failure, no route, ...) the probe is left uncached so that a
840840
# transient hiccup doesn't permanently mark the artifact as missing.
841841
def self.artifact_exists?(tarball_url)
842-
return false if tarball_url.include?("/#{UNPUBLISHED_MAVEN_VERSION}/")
842+
unpublished_version = Regexp.escape(UNPUBLISHED_MAVEN_VERSION)
843+
return false if tarball_url.match?(%r{/#{unpublished_version}(?:-SNAPSHOT)?/})
843844

844845
unless @@artifact_exists_cache.key?(tarball_url)
845846
# -L is used to follow redirects, useful for the nightlies

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

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -66,10 +66,7 @@ async function prepareHermesArtifactsAsync(
6666
// Resolve the version from the environment variable or use the default version
6767
let resolvedVersion = process.env.HERMES_VERSION ?? 'latest-v1';
6868

69-
if (
70-
resolvedVersion === 'latest-v1' ||
71-
!isMavenArtifactVersionPublished(resolvedVersion)
72-
) {
69+
if (resolvedVersion === 'latest-v1') {
7370
// TODO: rename 'latest-v1' to 'latest' once V1 is the only Hermes on npm
7471
hermesLog('Using latest-v1 tarball');
7572
const hermesVersion = await getLatestHermesVersionFromNPM();
@@ -211,6 +208,10 @@ async function findExistingTarballUrl(
211208
version /*: string */,
212209
buildType /*: BuildFlavor */,
213210
) /*: Promise<?string> */ {
211+
if (!isMavenArtifactVersionPublished(version)) {
212+
return null;
213+
}
214+
214215
const candidates = getTarballUrls(version, buildType);
215216
for (const url of candidates) {
216217
if (await hermesArtifactExists(url)) {
@@ -348,6 +349,11 @@ async function downloadHermesTarball(
348349
const tmpFile = `${artifactsPath}/hermes-ios.download`;
349350
try {
350351
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+
}
351357
hermesLog(`Downloading Hermes tarball from ${tarballUrl}`);
352358

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

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

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -50,10 +50,7 @@ async function prepareReactNativeDependenciesArtifactsAsync(
5050
// Resolve the version from the environment variable or use the default version
5151
let resolvedVersion = process.env.RN_DEP_VERSION ?? version;
5252

53-
if (
54-
resolvedVersion === 'nightly' ||
55-
!isMavenArtifactVersionPublished(resolvedVersion)
56-
) {
53+
if (resolvedVersion === 'nightly') {
5754
dependencyLog('Using latest nightly tarball');
5855
const rnVersion = await getNightlyVersionFromNPM();
5956
resolvedVersion = rnVersion;
@@ -240,6 +237,10 @@ async function findExistingTarballUrl(
240237
version /*: string */,
241238
buildType /*: BuildFlavor */,
242239
) /*: Promise<?string> */ {
240+
if (!isMavenArtifactVersionPublished(version)) {
241+
return null;
242+
}
243+
243244
const candidates = getTarballUrls(version, buildType);
244245
for (const url of candidates) {
245246
if (await reactNativeDependenciesArtifactExists(url)) {
@@ -405,6 +406,11 @@ async function downloadReactNativeDependenciesTarball(
405406
const tmpFile = `${artifactsPath}/reactnative-dependencies.download`;
406407
try {
407408
fs.mkdirSync(artifactsPath, {recursive: true});
409+
if (!isMavenArtifactVersionPublished(version)) {
410+
throw new Error(
411+
`Maven artifacts are not published for the development version ${version}`,
412+
);
413+
}
408414
dependencyLog(
409415
`Downloading ReactNativeDependencies tarball from ${tarballUrl}`,
410416
);

packages/react-native/scripts/spm/__tests__/download-spm-artifacts-test.js

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -378,11 +378,25 @@ describe('mavenRepositoryUrls', () => {
378378
// ---------------------------------------------------------------------------
379379

380380
describe('release URL builders', () => {
381-
it('does not create Maven URLs for the unpublished main version', () => {
381+
it('preserves Maven URLs for the unpublished main version', () => {
382382
expect(isMavenArtifactVersionPublished('1000.0.0')).toBe(false);
383-
expect(rnCoreReleaseUrls('1000.0.0', 'debug')).toEqual([]);
384-
expect(rnDepsReleaseUrls('1000.0.0', 'debug')).toEqual([]);
385-
expect(hermesReleaseUrls('1000.0.0', 'debug')).toEqual([]);
383+
expect(rnCoreReleaseUrls('1000.0.0', 'debug')).toHaveLength(2);
384+
expect(rnDepsReleaseUrls('1000.0.0', 'debug')).toHaveLength(2);
385+
expect(hermesReleaseUrls('1000.0.0', 'debug')).toHaveLength(2);
386+
});
387+
388+
it('does not probe Maven for the unpublished main version', async () => {
389+
const originalFetch = globalThis.fetch;
390+
globalThis.fetch = jest.fn();
391+
392+
try {
393+
await expect(
394+
exists(rnCoreReleaseUrls('1000.0.0', 'debug')[0]),
395+
).resolves.toBe(false);
396+
expect(globalThis.fetch).not.toHaveBeenCalled();
397+
} finally {
398+
globalThis.fetch = originalFetch;
399+
}
386400
});
387401

388402
it('rnCoreReleaseUrls builds a candidate per repository for the reactnative-core classifier', () => {

0 commit comments

Comments
 (0)