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..900acb417b05 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 @@ -14,6 +14,7 @@ import com.facebook.react.utils.PropertyUtils.INCLUDE_JITPACK_REPOSITORY import com.facebook.react.utils.PropertyUtils.INCLUDE_JITPACK_REPOSITORY_DEFAULT import com.facebook.react.utils.PropertyUtils.INTERNAL_HERMES_PUBLISHING_GROUP import com.facebook.react.utils.PropertyUtils.INTERNAL_HERMES_VERSION_NAME +import com.facebook.react.utils.PropertyUtils.INTERNAL_REACT_NATIVE_MAVEN_CACHE_ENABLED import com.facebook.react.utils.PropertyUtils.INTERNAL_REACT_NATIVE_MAVEN_LOCAL_REPO import com.facebook.react.utils.PropertyUtils.INTERNAL_REACT_PUBLISHING_GROUP import com.facebook.react.utils.PropertyUtils.INTERNAL_USE_HERMES_NIGHTLY @@ -27,6 +28,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 +77,17 @@ internal object DependencyUtils { repo.content { it.excludeGroup("org.webkit") } } } + // The React Native Maven cache must be added before Maven Central so it can serve cached + // artifacts first, while Maven Central remains the fallback. + if (!hasProperty(INTERNAL_REACT_NATIVE_MAVEN_LOCAL_REPO) && + isReactNativeMavenCacheEnabled()) { + 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") } @@ -271,6 +284,13 @@ internal object DependencyUtils { else -> INCLUDE_JITPACK_REPOSITORY_DEFAULT } + internal fun Project.isReactNativeMavenCacheEnabled(): Boolean = + when { + hasProperty(INTERNAL_REACT_NATIVE_MAVEN_CACHE_ENABLED) -> + property(INTERNAL_REACT_NATIVE_MAVEN_CACHE_ENABLED).toString().toBoolean() + else -> true + } + internal fun String.isNightly(): Boolean = this.startsWith("0.0.0") || "-nightly-" in this internal fun Project.exclusiveEnterpriseRepository() = diff --git a/packages/gradle-plugin/react-native-gradle-plugin/src/main/kotlin/com/facebook/react/utils/PropertyUtils.kt b/packages/gradle-plugin/react-native-gradle-plugin/src/main/kotlin/com/facebook/react/utils/PropertyUtils.kt index a35e5f37c5d1..111f1439128d 100644 --- a/packages/gradle-plugin/react-native-gradle-plugin/src/main/kotlin/com/facebook/react/utils/PropertyUtils.kt +++ b/packages/gradle-plugin/react-native-gradle-plugin/src/main/kotlin/com/facebook/react/utils/PropertyUtils.kt @@ -62,6 +62,10 @@ object PropertyUtils { */ const val INTERNAL_REACT_NATIVE_MAVEN_LOCAL_REPO = "react.internal.mavenLocalRepo" + /** Internal property that controls whether the React Native Maven cache is enabled. */ + const val INTERNAL_REACT_NATIVE_MAVEN_CACHE_ENABLED = + "react.internal.reactNativeMavenCacheEnabled" + /** * Internal property used to specify where the Windows Bash executable is located. This is useful * for contributors who are running Windows on their machine. 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..83dae9725d07 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,58 @@ 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_withReactNativeMavenCacheDisabled_doesNotContainMavenCache() { + val mavenCacheURI = URI.create("https://rnmaven.swmtest.xyz/") + val project = createProject() + project.extensions.extraProperties.set("react.internal.reactNativeMavenCacheEnabled", "false") + + 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/scripts/cocoapods/rncore.rb b/packages/react-native/scripts/cocoapods/rncore.rb index 252588c98432..a0ac7e050d30 100644 --- a/packages/react-native/scripts/cocoapods/rncore.rb +++ b/packages/react-native/scripts/cocoapods/rncore.rb @@ -364,16 +364,17 @@ 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 ReactNativePodsUtils.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.nightly_tarball_url(version, configuration, dsyms = false) @@ -475,7 +476,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..619244b4491b 100644 --- a/packages/react-native/scripts/cocoapods/rndependencies.rb +++ b/packages/react-native/scripts/cocoapods/rndependencies.rb @@ -165,16 +165,15 @@ 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 ReactNativePodsUtils.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.nightly_tarball_url(version, build_type) @@ -301,7 +300,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/cocoapods/utils.rb b/packages/react-native/scripts/cocoapods/utils.rb index 0e388d6a7ef5..98602455a672 100644 --- a/packages/react-native/scripts/cocoapods/utils.rb +++ b/packages/react-native/scripts/cocoapods/utils.rb @@ -12,11 +12,35 @@ # Utilities class for React Native Cocoapods class ReactNativePodsUtils + MAVEN_CENTRAL_REPOSITORY = "https://repo1.maven.org/maven2" + REACT_NATIVE_MAVEN_CACHE_REPOSITORY = "https://rnmaven.swmtest.xyz" + # URI::File.build validates path components as ASCII, so escape the filesystem path first. def self.local_file_uri(path) URI::File.build(path: URI::DEFAULT_PARSER.escape(path)).to_s 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 + + # Keep the React Native Maven cache before Maven Central so cached artifacts are tried first + # and Maven Central remains the fallback. + return react_native_maven_cache_enabled? ? + [REACT_NATIVE_MAVEN_CACHE_REPOSITORY, MAVEN_CENTRAL_REPOSITORY] : + [MAVEN_CENTRAL_REPOSITORY] + end + + def self.react_native_maven_cache_enabled?() + value = ENV['RCT_REACT_NATIVE_MAVEN_CACHE_ENABLED'] + return true if value == nil || value == "" + + value.downcase != "false" && value != "0" + end + def self.warn_if_not_on_arm64 if SysctlChecker.new().call_sysctl_arm64() == 1 && !Environment.new().ruby_platform().include?('arm64') Pod::UI.warn 'Do not use "pod install" from inside Rosetta2 (x86_64 emulation on arm64).' diff --git a/packages/react-native/scripts/ios-prebuild/hermes.js b/packages/react-native/scripts/ios-prebuild/hermes.js index eb3df6314352..c92848c458ba 100644 --- a/packages/react-native/scripts/ios-prebuild/hermes.js +++ b/packages/react-native/scripts/ios-prebuild/hermes.js @@ -8,7 +8,7 @@ * @format */ -const {createLogger} = require('./utils'); +const {createLogger, getMavenRepositoryUrls} = require('./utils'); const {execSync} = require('child_process'); const fs = require('fs'); const path = require('path'); @@ -17,6 +17,7 @@ const {promisify} = require('util'); const pipeline = promisify(stream.pipeline); const hermesLog = createLogger('Hermes'); +const artifactExistenceCache /*: Map */ = new Map(); /*:: import type {BuildFlavor, Destination, Platform} from './types'; @@ -187,16 +188,40 @@ 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`, + ); } /** @@ -205,13 +230,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 +261,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 +310,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 +320,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..05f2b6c18cc4 100644 --- a/packages/react-native/scripts/ios-prebuild/reactNativeDependencies.js +++ b/packages/react-native/scripts/ios-prebuild/reactNativeDependencies.js @@ -10,7 +10,11 @@ /*:: import type {BuildFlavor} from './types'; */ -const {computeNightlyTarballURL, createLogger} = require('./utils'); +const { + computeNightlyTarballURL, + createLogger, + getMavenRepositoryUrls, +} = require('./utils'); const {execSync} = require('child_process'); const fs = require('fs'); const path = require('path'); @@ -20,6 +24,7 @@ const {promisify} = require('util'); const pipeline = promisify(stream.pipeline); const dependencyLog = createLogger('ReactNativeDependencies'); +const artifactExistenceCache /*: Map */ = new Map(); /** * Downloads ReactNativeDependencies artifacts from the specified version and build type. If you want to specify a specific @@ -178,16 +183,40 @@ 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`, + ); } async function getNightlyTarballUrl( @@ -212,13 +241,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 +267,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 +309,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 +338,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/scripts/ios-prebuild/utils.js b/packages/react-native/scripts/ios-prebuild/utils.js index d1c60f838b52..6e017aa90152 100644 --- a/packages/react-native/scripts/ios-prebuild/utils.js +++ b/packages/react-native/scripts/ios-prebuild/utils.js @@ -13,6 +13,9 @@ const {execSync} = require('child_process'); const fs = require('fs'); +const MAVEN_CENTRAL_REPOSITORY = 'https://repo1.maven.org/maven2'; +const REACT_NATIVE_MAVEN_CACHE_REPOSITORY = 'https://rnmaven.swmtest.xyz'; + /** * Creates a folder if it does not exist * @param {string} folderPath - The path to the folder @@ -102,9 +105,34 @@ async function computeNightlyTarballURL( return finalUrl; } +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(/\/+$/, '')]; + } + + // Keep the React Native Maven cache before Maven Central so cached artifacts are tried first + // and Maven Central remains the fallback. + return isReactNativeMavenCacheEnabled() + ? [REACT_NATIVE_MAVEN_CACHE_REPOSITORY, MAVEN_CENTRAL_REPOSITORY] + : [MAVEN_CENTRAL_REPOSITORY]; +} + +function isReactNativeMavenCacheEnabled() /*: boolean */ { + const value = process.env.RCT_REACT_NATIVE_MAVEN_CACHE_ENABLED; + if (value == null || value === '') { + return true; + } + + return value.toLowerCase() !== 'false' && value !== '0'; +} + module.exports = { createFolderIfNotExists, throwIfOnEden, createLogger, computeNightlyTarballURL, + getMavenRepositoryUrls, }; diff --git a/packages/react-native/sdks/hermes-engine/hermes-utils.rb b/packages/react-native/sdks/hermes-engine/hermes-utils.rb index 288a5b638543..e48657238399 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,38 @@ 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" + # Keep the React Native Maven cache before Maven Central so cached artifacts are tried first + # and Maven Central remains the fallback. + return react_native_maven_cache_enabled? ? + [REACT_NATIVE_MAVEN_CACHE_REPOSITORY, MAVEN_CENTRAL_REPOSITORY] : + [MAVEN_CENTRAL_REPOSITORY] +end + +def react_native_maven_cache_enabled?() + value = ENV['RCT_REACT_NATIVE_MAVEN_CACHE_ENABLED'] + return true if value == nil || value == "" + + value.downcase != "false" && value != "0" end def download_stable_hermes(react_native_path, version, configuration)