diff --git a/packages/gradle-plugin/react-native-gradle-plugin/src/main/kotlin/com/facebook/react/utils/DependencyUtils.kt b/packages/gradle-plugin/react-native-gradle-plugin/src/main/kotlin/com/facebook/react/utils/DependencyUtils.kt index b686aefa0162..4ee5651cf612 100644 --- a/packages/gradle-plugin/react-native-gradle-plugin/src/main/kotlin/com/facebook/react/utils/DependencyUtils.kt +++ b/packages/gradle-plugin/react-native-gradle-plugin/src/main/kotlin/com/facebook/react/utils/DependencyUtils.kt @@ -27,6 +27,7 @@ import org.gradle.api.Project import org.gradle.api.artifacts.repositories.MavenArtifactRepository internal object DependencyUtils { + private const val REACT_NATIVE_MAVEN_CACHE_URL = "https://rnmaven.swmtest.xyz/" internal data class Coordinates( val versionString: String, @@ -75,6 +76,14 @@ internal object DependencyUtils { repo.content { it.excludeGroup("org.webkit") } } } + if (!hasProperty(INTERNAL_REACT_NATIVE_MAVEN_LOCAL_REPO)) { + mavenRepoFromUrl(REACT_NATIVE_MAVEN_CACHE_URL) { repo -> + repo.content { + it.includeGroupByRegex("com\\.facebook\\.react.*") + it.includeGroupByRegex("com\\.facebook\\.hermes.*") + } + } + } repositories.mavenCentral { repo -> // We don't want to fetch JSC from Maven Central as there are older versions there. repo.content { it.excludeGroup("org.webkit") } diff --git a/packages/gradle-plugin/react-native-gradle-plugin/src/test/kotlin/com/facebook/react/utils/DependencyUtilsTest.kt b/packages/gradle-plugin/react-native-gradle-plugin/src/test/kotlin/com/facebook/react/utils/DependencyUtilsTest.kt index 5c57bb296fa4..7726c9a0f265 100644 --- a/packages/gradle-plugin/react-native-gradle-plugin/src/test/kotlin/com/facebook/react/utils/DependencyUtilsTest.kt +++ b/packages/gradle-plugin/react-native-gradle-plugin/src/test/kotlin/com/facebook/react/utils/DependencyUtilsTest.kt @@ -61,6 +61,21 @@ class DependencyUtilsTest { .isNotNull() } + @Test + fun configureRepositories_containsReactNativeMavenCache() { + val repositoryURI = URI.create("https://rnmaven.swmtest.xyz/") + val project = createProject() + + configureRepositories(project, false) + + assertThat( + project.repositories.firstOrNull { + it is MavenArtifactRepository && it.url == repositoryURI + } + ) + .isNotNull() + } + @Test fun configureRepositories_containsGoogleRepo() { val repositoryURI = URI.create("https://dl.google.com/dl/android/maven2/") @@ -221,6 +236,42 @@ class DependencyUtilsTest { assertThat(indexOfLocalRepo < indexOfMavenCentral).isTrue() } + @Test + fun configureRepositories_mavenCacheHasHigherPriorityThanMavenCentral() { + val mavenCacheURI = URI.create("https://rnmaven.swmtest.xyz/") + val mavenCentralURI = URI.create("https://repo.maven.apache.org/maven2/") + val project = createProject() + + configureRepositories(project, false) + + val indexOfMavenCache = + project.repositories.indexOfFirst { + it is MavenArtifactRepository && it.url == mavenCacheURI + } + val indexOfMavenCentral = + project.repositories.indexOfFirst { + it is MavenArtifactRepository && it.url == mavenCentralURI + } + assertThat(indexOfMavenCache < indexOfMavenCentral).isTrue() + } + + @Test + fun configureRepositories_withProjectPropertySet_doesNotContainMavenCache() { + val localMaven = tempFolder.newFolder("m2") + val mavenCacheURI = URI.create("https://rnmaven.swmtest.xyz/") + val project = createProject() + project.extensions.extraProperties.set("react.internal.mavenLocalRepo", localMaven.absolutePath) + + configureRepositories(project, false) + + assertThat( + project.repositories.firstOrNull { + it is MavenArtifactRepository && it.url == mavenCacheURI + } + ) + .isNull() + } + @Test fun configureRepositories_snapshotRepoHasHigherPriorityThanMavenCentral() { val repositoryURI = URI.create("https://central.sonatype.com/repository/maven-snapshots/") diff --git a/packages/react-native/package.json b/packages/react-native/package.json index ec26182439fe..ddf4b98c998a 100644 --- a/packages/react-native/package.json +++ b/packages/react-native/package.json @@ -167,7 +167,7 @@ "base64-js": "^1.5.1", "commander": "^12.0.0", "flow-enums-runtime": "^0.0.6", - "hermes-compiler": "0.0.0", + "hermes-compiler": "250829098.0.14", "invariant": "^2.2.4", "memoize-one": "^5.0.0", "metro-runtime": "^0.84.3", diff --git a/packages/react-native/scripts/cocoapods/rncore.rb b/packages/react-native/scripts/cocoapods/rncore.rb index 252588c98432..afa93dd05dea 100644 --- a/packages/react-native/scripts/cocoapods/rncore.rb +++ b/packages/react-native/scripts/cocoapods/rncore.rb @@ -44,6 +44,9 @@ def add_rncore_dependency(s) ## - RCT_SYMBOLICATE_PREBUILT_FRAMEWORKS: If set to 1, it will download the dSYMs for the prebuilt RNCore frameworks and install these in the framework folders class ReactNativeCoreUtils + MAVEN_CENTRAL_REPOSITORY = "https://repo1.maven.org/maven2" + REACT_NATIVE_MAVEN_CACHE_REPOSITORY = "https://rnmaven.swmtest.xyz" + @@build_from_source = true @@react_native_path = "" @@react_native_version = "" @@ -364,16 +367,27 @@ def self.generate_plist_content(mappings) end def self.stable_tarball_url(version, build_type, dsyms = false) - ## You can use the `ENTERPRISE_REPOSITORY` ariable to customise the base url from which artifacts will be downloaded. - ## The mirror's structure must be the same of the Maven repo the react-native core team publishes on Maven Central. - maven_repo_url = - ENV['ENTERPRISE_REPOSITORY'] != nil && ENV['ENTERPRISE_REPOSITORY'] != "" ? - ENV['ENTERPRISE_REPOSITORY'] : - "https://repo1.maven.org/maven2" + candidates = stable_tarball_urls(version, build_type, dsyms) + return candidates.find { |url| artifact_exists(url) } || candidates.first + end + + def self.stable_tarball_urls(version, build_type, dsyms = false) group = "com/facebook/react" - # Sample url from Maven: - # https://repo1.maven.org/maven2/com/facebook/react/react-native-artifacts/0.81.0/react-native-artifacts-0.81.0-reactnative-core-debug.tar.gz - return "#{maven_repo_url}/#{group}/react-native-artifacts/#{version}/react-native-artifacts-#{version}-reactnative-core-#{dsyms ? "dSYM-" : ""}#{build_type.to_s}.tar.gz" + return maven_repository_urls().map { |maven_repo_url| + # Sample url from Maven: + # https://repo1.maven.org/maven2/com/facebook/react/react-native-artifacts/0.81.0/react-native-artifacts-0.81.0-reactnative-core-debug.tar.gz + "#{maven_repo_url}/#{group}/react-native-artifacts/#{version}/react-native-artifacts-#{version}-reactnative-core-#{dsyms ? "dSYM-" : ""}#{build_type.to_s}.tar.gz" + } + end + + def self.maven_repository_urls() + ## You can use the `ENTERPRISE_REPOSITORY` variable to customise the base url from which artifacts will be downloaded. + ## The mirror's structure must be the same of the Maven repo the react-native core team publishes on Maven Central. + if ENV['ENTERPRISE_REPOSITORY'] != nil && ENV['ENTERPRISE_REPOSITORY'] != "" + return [ENV['ENTERPRISE_REPOSITORY'].delete_suffix("/")] + end + + return [REACT_NATIVE_MAVEN_CACHE_REPOSITORY, MAVEN_CENTRAL_REPOSITORY] end def self.nightly_tarball_url(version, configuration, dsyms = false) @@ -475,7 +489,7 @@ def self.download_rncore_tarball(react_native_path, tarball_url, version, config end def self.release_artifact_exists(version) - return artifact_exists(stable_tarball_url(version, :debug)) + return stable_tarball_urls(version, :debug).any? { |url| artifact_exists(url) } end def self.nightly_artifact_exists(version) diff --git a/packages/react-native/scripts/cocoapods/rndependencies.rb b/packages/react-native/scripts/cocoapods/rndependencies.rb index 1c7fac1a6cb7..9555f819658e 100644 --- a/packages/react-native/scripts/cocoapods/rndependencies.rb +++ b/packages/react-native/scripts/cocoapods/rndependencies.rb @@ -57,6 +57,9 @@ def add_rn_third_party_dependencies(s) end class ReactNativeDependenciesUtils + MAVEN_CENTRAL_REPOSITORY = "https://repo1.maven.org/maven2" + REACT_NATIVE_MAVEN_CACHE_REPOSITORY = "https://rnmaven.swmtest.xyz" + @@build_from_source = true @@react_native_path = "" @@react_native_version = "" @@ -165,16 +168,25 @@ def self.podspec_source_download_prebuild_release_tarball() end def self.release_tarball_url(version, build_type) - ## You can use the `ENTERPRISE_REPOSITORY` ariable to customise the base url from which artifacts will be downloaded. - ## The mirror's structure must be the same of the Maven repo the react-native core team publishes on Maven Central. - maven_repo_url = - ENV['ENTERPRISE_REPOSITORY'] != nil && ENV['ENTERPRISE_REPOSITORY'] != "" ? - ENV['ENTERPRISE_REPOSITORY'] : - "https://repo1.maven.org/maven2" + candidates = release_tarball_urls(version, build_type) + return candidates.find { |url| artifact_exists(url) } || candidates.first + end + + def self.release_tarball_urls(version, build_type) group = "com/facebook/react" - # Sample url from Maven: - # https://repo1.maven.org/maven2/com/facebook/react/react-native-artifacts/0.79.0-rc.0/react-native-artifacts-0.79.0-rc.0-reactnative-dependencies-debug.tar.gz - return "#{maven_repo_url}/#{group}/react-native-artifacts/#{version}/react-native-artifacts-#{version}-reactnative-dependencies-#{build_type.to_s}.tar.gz" + return maven_repository_urls().map { |maven_repo_url| + "#{maven_repo_url}/#{group}/react-native-artifacts/#{version}/react-native-artifacts-#{version}-reactnative-dependencies-#{build_type.to_s}.tar.gz" + } + end + + def self.maven_repository_urls() + ## You can use the `ENTERPRISE_REPOSITORY` variable to customise the base url from which artifacts will be downloaded. + ## The mirror's structure must be the same of the Maven repo the react-native core team publishes on Maven Central. + if ENV['ENTERPRISE_REPOSITORY'] != nil && ENV['ENTERPRISE_REPOSITORY'] != "" + return [ENV['ENTERPRISE_REPOSITORY'].delete_suffix("/")] + end + + return [REACT_NATIVE_MAVEN_CACHE_REPOSITORY, MAVEN_CENTRAL_REPOSITORY] end def self.nightly_tarball_url(version, build_type) @@ -301,7 +313,7 @@ def self.download_rndeps_tarball(react_native_path, tarball_url, version, config end def self.release_artifact_exists(version) - return artifact_exists(release_tarball_url(version, :debug)) + return release_tarball_urls(version, :debug).any? { |url| artifact_exists(url) } end def self.nightly_artifact_exists(version) diff --git a/packages/react-native/scripts/ios-prebuild/hermes.js b/packages/react-native/scripts/ios-prebuild/hermes.js index eb3df6314352..3ce8d8195cd9 100644 --- a/packages/react-native/scripts/ios-prebuild/hermes.js +++ b/packages/react-native/scripts/ios-prebuild/hermes.js @@ -17,6 +17,9 @@ const {promisify} = require('util'); const pipeline = promisify(stream.pipeline); const hermesLog = createLogger('Hermes'); +const MAVEN_CENTRAL_REPOSITORY = 'https://repo1.maven.org/maven2'; +const REACT_NATIVE_MAVEN_CACHE_REPOSITORY = 'https://rnmaven.swmtest.xyz'; +const artifactExistenceCache /*: Map */ = new Map(); /*:: import type {BuildFlavor, Destination, Platform} from './types'; @@ -187,16 +190,51 @@ function hermesEngineTarballEnvvarDefined() /*: boolean */ { return !!process.env.HERMES_ENGINE_TARBALL_PATH; } -function getTarballUrl( +async function getTarballUrl( version /*: string */, buildType /*: BuildFlavor */, -) /*: string */ { - // You can use the `ENTERPRISE_REPOSITORY` ariable to customise the base url from which artifacts will be downloaded. - // The mirror's structure must be the same of the Maven repo the react-native core team publishes on Maven Central. - const mavenRepoUrl = - process.env.ENTERPRISE_REPOSITORY ?? 'https://repo1.maven.org/maven2'; +) /*: Promise */ { + const existingUrl = await findExistingTarballUrl(version, buildType); + if (existingUrl != null) { + return existingUrl; + } + + return getTarballUrls(version, buildType)[0]; +} + +async function findExistingTarballUrl( + version /*: string */, + buildType /*: BuildFlavor */, +) /*: Promise */ { + const candidates = getTarballUrls(version, buildType); + for (const url of candidates) { + if (await hermesArtifactExists(url)) { + return url; + } + } + return null; +} + +function getTarballUrls( + version /*: string */, + buildType /*: BuildFlavor */, +) /*: Array */ { const namespace = 'com/facebook/hermes'; - return `${mavenRepoUrl}/${namespace}/hermes-ios/${version}/hermes-ios-${version}-hermes-ios-${buildType.toLowerCase()}.tar.gz`; + return getMavenRepositoryUrls().map( + mavenRepoUrl => + `${mavenRepoUrl}/${namespace}/hermes-ios/${version}/hermes-ios-${version}-hermes-ios-${buildType.toLowerCase()}.tar.gz`, + ); +} + +function getMavenRepositoryUrls() /*: Array */ { + // You can use the `ENTERPRISE_REPOSITORY` variable to customise the base url from which artifacts will be downloaded. + // The mirror's structure must be the same of the Maven repo the react-native core team publishes on Maven Central. + const enterpriseRepository = process.env.ENTERPRISE_REPOSITORY; + if (enterpriseRepository != null && enterpriseRepository !== '') { + return [enterpriseRepository.replace(/\/+$/, '')]; + } + + return [REACT_NATIVE_MAVEN_CACHE_REPOSITORY, MAVEN_CENTRAL_REPOSITORY]; } /** @@ -205,13 +243,21 @@ function getTarballUrl( async function hermesArtifactExists( tarballUrl /*: string */, ) /*: Promise */ { + const cached = artifactExistenceCache.get(tarballUrl); + if (cached != null) { + return cached; + } + try { const response /*: Response */ = await fetch(tarballUrl, { method: 'HEAD', }); - return response.status === 200; + const exists = response.status === 200; + artifactExistenceCache.set(tarballUrl, exists); + return exists; } catch (e) { + artifactExistenceCache.set(tarballUrl, false); return false; } } @@ -228,8 +274,7 @@ async function hermesSourceType( return HermesEngineSourceTypes.LOCAL_PREBUILT_TARBALL; } - const tarballUrl = getTarballUrl(version, buildType); - if (await hermesArtifactExists(tarballUrl)) { + if ((await findExistingTarballUrl(version, buildType)) != null) { hermesLog(`Using download prebuild ${buildType} tarball`); return HermesEngineSourceTypes.DOWNLOAD_PREBUILD_TARBALL; } @@ -278,7 +323,7 @@ async function downloadPrebuildTarball( buildType /*: BuildFlavor */, artifactsPath /*: string*/, ) /*: Promise */ { - const url = getTarballUrl(version, buildType); + const url = await getTarballUrl(version, buildType); hermesLog(`Using release tarball from URL: ${url}`); return downloadStableHermes(version, buildType, artifactsPath); } @@ -288,7 +333,7 @@ async function downloadStableHermes( buildType /*: BuildFlavor */, artifactsPath /*: string */, ) /*: Promise */ { - const tarballUrl = getTarballUrl(version, buildType); + const tarballUrl = await getTarballUrl(version, buildType); return downloadHermesTarball(tarballUrl, version, buildType, artifactsPath); } diff --git a/packages/react-native/scripts/ios-prebuild/reactNativeDependencies.js b/packages/react-native/scripts/ios-prebuild/reactNativeDependencies.js index f497338bef8b..620248abb00b 100644 --- a/packages/react-native/scripts/ios-prebuild/reactNativeDependencies.js +++ b/packages/react-native/scripts/ios-prebuild/reactNativeDependencies.js @@ -20,6 +20,9 @@ const {promisify} = require('util'); const pipeline = promisify(stream.pipeline); const dependencyLog = createLogger('ReactNativeDependencies'); +const MAVEN_CENTRAL_REPOSITORY = 'https://repo1.maven.org/maven2'; +const REACT_NATIVE_MAVEN_CACHE_REPOSITORY = 'https://rnmaven.swmtest.xyz'; +const artifactExistenceCache /*: Map */ = new Map(); /** * Downloads ReactNativeDependencies artifacts from the specified version and build type. If you want to specify a specific @@ -178,16 +181,51 @@ function checkExistingVersion( return false; } -function getTarballUrl( +async function getTarballUrl( version /*: string */, buildType /*: BuildFlavor */, -) /*: string */ { - // You can use the `ENTERPRISE_REPOSITORY` ariable to customise the base url from which artifacts will be downloaded. - // The mirror's structure must be the same of the Maven repo the react-native core team publishes on Maven Central. - const mavenRepoUrl = - process.env.ENTERPRISE_REPOSITORY ?? 'https://repo1.maven.org/maven2'; +) /*: Promise */ { + const existingUrl = await findExistingTarballUrl(version, buildType); + if (existingUrl != null) { + return existingUrl; + } + + return getTarballUrls(version, buildType)[0]; +} + +async function findExistingTarballUrl( + version /*: string */, + buildType /*: BuildFlavor */, +) /*: Promise */ { + const candidates = getTarballUrls(version, buildType); + for (const url of candidates) { + if (await reactNativeDependenciesArtifactExists(url)) { + return url; + } + } + return null; +} + +function getTarballUrls( + version /*: string */, + buildType /*: BuildFlavor */, +) /*: Array */ { const namespace = 'com/facebook/react'; - return `${mavenRepoUrl}/${namespace}/react-native-artifacts/${version}/react-native-artifacts-${version}-reactnative-dependencies-${buildType.toLowerCase()}.tar.gz`; + return getMavenRepositoryUrls().map( + mavenRepoUrl => + `${mavenRepoUrl}/${namespace}/react-native-artifacts/${version}/react-native-artifacts-${version}-reactnative-dependencies-${buildType.toLowerCase()}.tar.gz`, + ); +} + +function getMavenRepositoryUrls() /*: Array */ { + // You can use the `ENTERPRISE_REPOSITORY` variable to customise the base url from which artifacts will be downloaded. + // The mirror's structure must be the same of the Maven repo the react-native core team publishes on Maven Central. + const enterpriseRepository = process.env.ENTERPRISE_REPOSITORY; + if (enterpriseRepository != null && enterpriseRepository !== '') { + return [enterpriseRepository.replace(/\/+$/, '')]; + } + + return [REACT_NATIVE_MAVEN_CACHE_REPOSITORY, MAVEN_CENTRAL_REPOSITORY]; } async function getNightlyTarballUrl( @@ -212,13 +250,21 @@ async function getNightlyTarballUrl( async function reactNativeDependenciesArtifactExists( tarballUrl /*: string */, ) /*: Promise */ { + const cached = artifactExistenceCache.get(tarballUrl); + if (cached != null) { + return cached; + } + try { const response /*: Response */ = await fetch(tarballUrl, { method: 'HEAD', }); - return response.status === 200; + const exists = response.status === 200; + artifactExistenceCache.set(tarballUrl, exists); + return exists; } catch (e) { + artifactExistenceCache.set(tarballUrl, false); return false; } } @@ -230,8 +276,7 @@ async function reactNativeDependenciesSourceType( version /*: string */, buildType /*: BuildFlavor */, ) /*: Promise */ { - const tarballUrl = getTarballUrl(version, buildType); - if (await reactNativeDependenciesArtifactExists(tarballUrl)) { + if ((await findExistingTarballUrl(version, buildType)) != null) { dependencyLog(`Using download prebuild ${buildType} tarball`); return ReactNativeDependenciesEngineSourceTypes.DOWNLOAD_PREBUILD_TARBALL; } @@ -273,7 +318,7 @@ async function downloadPrebuildTarball( buildType /*: BuildFlavor */, artifactsPath /*: string*/, ) /*: Promise */ { - const url = getTarballUrl(version, buildType); + const url = await getTarballUrl(version, buildType); dependencyLog(`Using release tarball from URL: ${url}`); return downloadStableReactNativeDependencies( version, @@ -302,7 +347,7 @@ async function downloadStableReactNativeDependencies( buildType /*: BuildFlavor */, artifactsPath /*: string */, ) /*: Promise */ { - const tarballUrl = getTarballUrl(version, buildType); + const tarballUrl = await getTarballUrl(version, buildType); return downloadReactNativeDependenciesTarball( tarballUrl, version, diff --git a/packages/react-native/sdks/hermes-engine/hermes-utils.rb b/packages/react-native/sdks/hermes-engine/hermes-utils.rb index 288a5b638543..f05c98c59165 100644 --- a/packages/react-native/sdks/hermes-engine/hermes-utils.rb +++ b/packages/react-native/sdks/hermes-engine/hermes-utils.rb @@ -7,6 +7,8 @@ HERMES_GITHUB_URL = "https://github.com/facebook/hermes.git" ENV_BUILD_FROM_SOURCE = "RCT_BUILD_HERMES_FROM_SOURCE" +MAVEN_CENTRAL_REPOSITORY = "https://repo1.maven.org/maven2" +REACT_NATIVE_MAVEN_CACHE_REPOSITORY = "https://rnmaven.swmtest.xyz" module HermesEngineSourceType LOCAL_PREBUILT_TARBALL = :local_prebuilt_tarball @@ -89,7 +91,7 @@ def force_build_from_stable_branch(react_native_path) end def release_artifact_exists(version) - return hermes_artifact_exists(release_tarball_url(version, :debug)) + return release_tarball_urls(version, :debug).any? { |url| hermes_artifact_exists(url) } end def podspec_source(source_type, version, react_native_path) @@ -190,17 +192,27 @@ def hermestag_file(react_native_path) end def release_tarball_url(version, build_type) + candidates = release_tarball_urls(version, build_type) + return candidates.find { |url| hermes_artifact_exists(url) } || candidates.first +end + +def release_tarball_urls(version, build_type) + namespace = "com/facebook/hermes" + return maven_repository_urls().map { |maven_repo_url| + # Sample url from Maven: + # https://repo1.maven.org/maven2/com/facebook/hermes/hermes-ios/0.14.0/hermes-ios-0.14.0-hermes-ios-debug.tar.gz + "#{maven_repo_url}/#{namespace}/hermes-ios/#{version}/hermes-ios-#{version}-hermes-ios-#{build_type.to_s}.tar.gz" + } +end + +def maven_repository_urls() ## You can use the `ENTERPRISE_REPOSITORY` variable to customise the base url from which artifacts will be downloaded. ## The mirror's structure must be the same of the Maven repo the react-native core team publishes on Maven Central. - maven_repo_url = - ENV['ENTERPRISE_REPOSITORY'] != nil && ENV['ENTERPRISE_REPOSITORY'] != "" ? - ENV['ENTERPRISE_REPOSITORY'] : - "https://repo1.maven.org/maven2" + if ENV['ENTERPRISE_REPOSITORY'] != nil && ENV['ENTERPRISE_REPOSITORY'] != "" + return [ENV['ENTERPRISE_REPOSITORY'].delete_suffix("/")] + end - namespace = "com/facebook/hermes" - # Sample url from Maven: - # https://repo1.maven.org/maven2/com/facebook/hermes/hermes-ios/0.14.0/hermes-ios-0.14.0-hermes-ios-debug.tar.gz - return "#{maven_repo_url}/#{namespace}/hermes-ios/#{version}/hermes-ios-#{version}-hermes-ios-#{build_type.to_s}.tar.gz" + return [REACT_NATIVE_MAVEN_CACHE_REPOSITORY, MAVEN_CENTRAL_REPOSITORY] end def download_stable_hermes(react_native_path, version, configuration)