Skip to content

Commit f134124

Browse files
cipolleschifacebook-github-bot
authored andcommitted
Cache prebuilt iOS binaries in ~/Library/Caches/ReactNative (#56847)
Summary: Adds a shared cache layer at `~/Library/Caches/ReactNative/` for prebuilt iOS tarballs (Hermes, ReactNativeDependencies, ReactNativeCore). - On first download, tarballs are saved to both the local Pods artifacts dir **and** the shared cache. - On subsequent `pod install` runs, if the local Pods cache is empty but the shared cache has the tarball for that version, it is copied locally instead of re-downloaded from Maven. - Adds descriptive log messages for each scenario: local Pods hit, shared cache hit, and cache miss (download). This avoids redundant downloads when: - The Pods directory is deleted between installs - Multiple projects use the same React Native version - CI environments have a persistent home directory ## Changelog: [IOS] [CHANGED] - Cache prebuilt iOS binaries (Hermes, ReactNativeDependencies, ReactNativeCore) in ~/Library/Caches/ReactNative to avoid redundant downloads across pod installs Test Plan: ### Cold start ==> Downloading ``` [ReactNativeDependencies] Setting up ReactNativeDependencies... [ReactNativeDependencies] Building from source: false [ReactNativeDependencies] Using release tarball [ReactNativeDependencies] Using tarball from URL: https://repo1.maven.org/maven2/com/facebook/react/react-native-artifacts/0.81.6/react-native-artifacts-0.81.6-reactnative-dependencies-debug.tar.gz [ReactNativeDependencies] Cache miss: downloading reactnative-dependencies-0.81.6-debug.tar.gz from https://repo1.maven.org/maven2/com/facebook/react/react-native-artifacts/0.81.6/react-native-artifacts-0.81.6-reactnative-dependencies-debug.tar.gz % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 0 0 0 0 0 0 0 0 --:--:-- --:- 0 0 0 0 0 0 0 0 --:--:-- --:- 35 18.3M 35 6580k 0 0 6386k 0 0:00:02 0:0 73 18.3M 73 13.5M 0 0 6827k 0 0:00:02 0:0100 18.3M 100 18.3M 0 0 6915k 0 0:00:02 0:00:02 --:--:-- 6914k [ReactNativeDependencies] Saved reactnative-dependencies-0.81.6-debug.tar.gz to shared cache (/Users/cipolleschi/Library/Caches/ReactNative) [ReactNativeDependencies] Cache miss: downloading reactnative-dependencies-0.81.6-release.tar.gz from https://repo1.maven.org/maven2/com/facebook/react/react-native-artifacts/0.81.6/react-native-artifacts-0.81.6-reactnative-dependencies-release.tar.gz % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 0 0 0 0 0 0 0 0 --:--:-- --:- 0 0 0 0 0 0 0 0 --:--:-- --:- 67 9.9M 67 6948k 0 0 6332k 0 0:00:01 0:0100 9.9M 100 9.9M 0 0 6607k 0 0:00:01 0:00:01 --:--:-- 6603k [ReactNativeDependencies] Saved reactnative-dependencies-0.81.6-release.tar.gz to shared cache (/Users/cipolleschi/Library/Caches/ReactNative) [ReactNativeDependencies] Source: {http: "https://repo1.maven.org/maven2/com/facebook/react/react-native-artifacts/0.81.6/react-native-artifacts-0.81.6-reactnative-dependencies-debug.tar.gz"} [ReactNativeCore] Setting up ReactNativeCore... [ReactNativeCore] Building from source: false [ReactNativeCore] Using tarball from URL: https://repo1.maven.org/maven2/com/facebook/react/react-native-artifacts/0.81.6/react-native-artifacts-0.81.6-reactnative-core-debug.tar.gz [ReactNativeCore] Cache miss: downloading reactnative-core-0.81.6-debug.tar.gz from https://repo1.maven.org/maven2/com/facebook/react/react-native-artifacts/0.81.6/react-native-artifacts-0.81.6-reactnative-core-debug.tar.gz % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 0 0 0 0 0 0 0 0 --:--:-- --:- 1 78.5M 1 1557k 0 0 5035k 0 0:00:15 --:- 10 78.5M 10 8537k 0 0 6536k 0 0:00:12 0:0 19 78.5M 19 15.4M 0 0 6866k 0 0:00:11 0:0 28 78.5M 28 22.5M 0 0 6990k 0 0:00:11 0:0 37 78.5M 37 29.7M 0 0 7062k 0 0:00:11 0:0 46 78.5M 46 36.4M 0 0 7037k 0 0:00:11 0:0 55 78.5M 55 43.5M 0 0 7065k 0 0:00:11 0:0 64 78.5M 64 50.5M 0 0 7077k 0 0:00:11 0:0100 78.5M 100 78.5M 0 0 6852k 0 0:00:11 0:00:11 --:--:-- 6482k [ReactNativeCore] Saved reactnative-core-0.81.6-debug.tar.gz to shared cache (/Users/cipolleschi/Library/Caches/ReactNative) [ReactNativeCore] Cache miss: downloading reactnative-core-0.81.6-release.tar.gz from https://repo1.maven.org/maven2/com/facebook/react/react-native-artifacts/0.81.6/react-native-artifacts-0.81.6-reactnative-core-release.tar.gz % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 100 26.3M 100 26.3M 0 0 5683k 0 0:00:04 0:00:04 --:--:-- 5793k [ReactNativeCore] Saved reactnative-core-0.81.6-release.tar.gz to shared cache (/Users/cipolleschi/Library/Caches/ReactNative) [ReactNativeCore] Source: {http: "https://repo1.maven.org/maven2/com/facebook/react/react-native-artifacts/0.81.6/react-native-artifacts-0.81.6-reactnative-core-debug.tar.gz"} Configuring the target with the New Architecture [ReactNativeCore] Using React Native Core and React Native Dependencies prebuilt versions. [Codegen] Analyzing /Users/cipolleschi/Tests/My0_81App/package.json ``` ### Warn shared cache, Empty Pods dir ==> Copy ``` [ReactNativeDependencies] Setting up ReactNativeDependencies... [ReactNativeDependencies] Building from source: false [ReactNativeDependencies] Using release tarball [ReactNativeDependencies] Using tarball from URL: https://repo1.maven.org/maven2/com/facebook/react/react-native-artifacts/0.81.6/react-native-artifacts-0.81.6-reactnative-dependencies-debug.tar.gz [ReactNativeDependencies] Cache hit: copying reactnative-dependencies-0.81.6-debug.tar.gz from shared cache (/Users/cipolleschi/Library/Caches/ReactNative) [ReactNativeDependencies] Cache hit: copying reactnative-dependencies-0.81.6-release.tar.gz from shared cache (/Users/cipolleschi/Library/Caches/ReactNative) [ReactNativeDependencies] Source: {http: "https://repo1.maven.org/maven2/com/facebook/react/react-native-artifacts/0.81.6/react-native-artifacts-0.81.6-reactnative-dependencies-debug.tar.gz"} [ReactNativeCore] Setting up ReactNativeCore... [ReactNativeCore] Building from source: false [ReactNativeCore] Using tarball from URL: https://repo1.maven.org/maven2/com/facebook/react/react-native-artifacts/0.81.6/react-native-artifacts-0.81.6-reactnative-core-debug.tar.gz [ReactNativeCore] Cache hit: copying reactnative-core-0.81.6-debug.tar.gz from shared cache (/Users/cipolleschi/Library/Caches/ReactNative) [ReactNativeCore] Cache hit: copying reactnative-core-0.81.6-release.tar.gz from shared cache (/Users/cipolleschi/Library/Caches/ReactNative) [ReactNativeCore] Source: {http: "https://repo1.maven.org/maven2/com/facebook/react/react-native-artifacts/0.81.6/react-native-artifacts-0.81.6-reactnative-core-debug.tar.gz"} Configuring the target with the New Architecture [ReactNativeCore] Using React Native Core and React Native Dependencies prebuilt versions. [Codegen] Analyzing /Users/cipolleschi/Tests/My0_81App/package.json ... [ReactNativeDependencies] Using release tarball [ReactNativeDependencies] Using tarball from URL: https://repo1.maven.org/maven2/com/facebook/react/react-native-artifacts/0.81.6/react-native-artifacts-0.81.6-reactnative-dependencies-debug.tar.gz [ReactNativeDependencies] Tarball reactnative-dependencies-0.81.6-debug.tar.gz already exists in Pods. Skipping download. [ReactNativeDependencies] Tarball reactnative-dependencies-0.81.6-release.tar.gz already exists in Pods. Skipping download. ``` ### Warm Cache, Pods folder populated ==> Nothing happens ``` [ReactNativeDependencies] Setting up ReactNativeDependencies... [ReactNativeDependencies] Building from source: false [ReactNativeDependencies] Using release tarball [ReactNativeDependencies] Using tarball from URL: https://repo1.maven.org/maven2/com/facebook/react/react-native-artifacts/0.81.6/react-native-artifacts-0.81.6-reactnative-dependencies-debug.tar.gz [ReactNativeDependencies] Tarball reactnative-dependencies-0.81.6-debug.tar.gz already exists in Pods. Skipping download. [ReactNativeDependencies] Tarball reactnative-dependencies-0.81.6-release.tar.gz already exists in Pods. Skipping download. [ReactNativeDependencies] Source: {http: "https://repo1.maven.org/maven2/com/facebook/react/react-native-artifacts/0.81.6/react-native-artifacts-0.81.6-reactnative-dependencies-debug.tar.gz"} [ReactNativeCore] Setting up ReactNativeCore... [ReactNativeCore] Building from source: false [ReactNativeCore] Using tarball from URL: https://repo1.maven.org/maven2/com/facebook/react/react-native-artifacts/0.81.6/react-native-artifacts-0.81.6-reactnative-core-debug.tar.gz [ReactNativeCore] Tarball reactnative-core-0.81.6-debug.tar.gz already exists in Pods. Skipping download. [ReactNativeCore] Tarball reactnative-core-0.81.6-release.tar.gz already exists in Pods. Skipping download. [ReactNativeCore] Source: {http: "https://repo1.maven.org/maven2/com/facebook/react/react-native-artifacts/0.81.6/react-native-artifacts-0.81.6-reactnative-core-debug.tar.gz"} Configuring the target with the New Architecture [ReactNativeCore] Using React Native Core and React Native Dependencies prebuilt versions. [Codegen] Analyzing /Users/cipolleschi/Tests/My0_81App/package.json [Codegen] Searching for codegen-enabled libraries in the app. [Codegen] The "codegenConfig" field is not defined in package.json. Assuming there is nothing to generate at the app level. [Codegen] Searching for codegen-enabled libraries in react-native.config.js [Codegen] Found react-native-safe-area-context [Codegen] Processing safeareacontext [Codegen] Searching for podspec in the project dependencies. [Codegen] Supported Apple platforms: ios, macos, tvos, visionos for safeareacontext [Codegen] Generating Native Code for safeareacontext - ios [Codegen] Generated artifacts: /Users/cipolleschi/Tests/My0_81App/ios/build/generated/ios [Codegen] Generating RCTThirdPartyComponentsProvider.h [Codegen] Generated artifact: /Users/cipolleschi/Tests/My0_81App/ios/build/generated/ios/RCTThirdPartyComponentsProvider.h [Codegen] Generating RCTThirdPartyComponentsProvider.mm [Codegen] Generated artifact: /Users/cipolleschi/Tests/My0_81App/ios/build/generated/ios/RCTThirdPartyComponentsProvider.mm [Codegen] Generating RCTModulesProvider.h [Codegen] Generated artifact: /Users/cipolleschi/Tests/My0_81App/ios/build/generated/ios/RCTModuleProviders.h [Codegen] Generating RCTModuleProviders.mm [Codegen] Generated artifact: /Users/cipolleschi/Tests/My0_81App/ios/build/generated/ios/RCTModuleProviders.mm [Codegen] Generating RCTAppDependencyProvider [Codegen] Generated artifact: /Users/cipolleschi/Tests/My0_81App/ios/build/generated/ios/RCTAppDependencyProvider.h [Codegen] Generated artifact: /Users/cipolleschi/Tests/My0_81App/ios/build/generated/ios/RCTAppDependencyProvider.mm [Codegen] Generated podspec: /Users/cipolleschi/Tests/My0_81App/ios/build/generated/ios/ReactAppDependencyProvider.podspec [Codegen] Generated podspec: /Users/cipolleschi/Tests/My0_81App/ios/build/generated/ios/ReactCodegen.podspec [Codegen] Done. [ReactNativeDependencies] Using release tarball [ReactNativeDependencies] Using tarball from URL: https://repo1.maven.org/maven2/com/facebook/react/react-native-artifacts/0.81.6/react-native-artifacts-0.81.6-reactnative-dependencies-debug.tar.gz [ReactNativeDependencies] Tarball reactnative-dependencies-0.81.6-debug.tar.gz already exists in Pods. Skipping download. [ReactNativeDependencies] Tarball reactnative-dependencies-0.81.6-release.tar.gz already exists in Pods. Skipping download. Analyzing dependencies ``` Reviewed By: cortinico Differential Revision: D105315202 Pulled By: cipolleschi
1 parent 3b76474 commit f134124

5 files changed

Lines changed: 233 additions & 18 deletions

File tree

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

Lines changed: 51 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -406,17 +406,60 @@ def self.download_nightly_rncore(react_native_path, version, configuration, dsym
406406
end
407407

408408
def self.download_rncore_tarball(react_native_path, tarball_url, version, configuration, dsyms = false)
409-
destination_path = configuration == nil ?
410-
"#{artifacts_dir()}/reactnative-core-#{version}#{dsyms ? "-dSYM" : ""}.tar.gz" :
411-
"#{artifacts_dir()}/reactnative-core-#{version}#{dsyms ? "-dSYM" : ""}-#{configuration}.tar.gz"
409+
filename = configuration == nil ?
410+
"reactnative-core-#{version}#{dsyms ? "-dSYM" : ""}.tar.gz" :
411+
"reactnative-core-#{version}#{dsyms ? "-dSYM" : ""}-#{configuration}.tar.gz"
412+
destination_path = "#{artifacts_dir()}/#{filename}"
413+
414+
if File.exist?(destination_path)
415+
rncore_log("Tarball #{filename} already exists in Pods. Skipping download.")
416+
return destination_path
417+
end
412418

413-
unless File.exist?(destination_path)
414-
# Download to a temporary file first so we don't cache incomplete downloads.
415-
rncore_log("Downloading ReactNativeCore-prebuilt #{dsyms ? "dSYMs " : ""}#{configuration ? configuration.to_s : ""} tarball from #{tarball_url} to #{Pathname.new(destination_path).relative_path_from(Pathname.pwd).to_s}")
419+
`mkdir -p "#{artifacts_dir()}"`
420+
421+
if ReactNativePodsUtils.skip_caches?
422+
rncore_log("RCT_SKIP_CACHES is set. Downloading #{filename} directly (bypassing shared cache).")
416423
tmp_file = "#{artifacts_dir()}/reactnative-core.download"
417-
`mkdir -p "#{artifacts_dir()}" && curl "#{tarball_url}" -Lo "#{tmp_file}" && mv "#{tmp_file}" "#{destination_path}"`
424+
`curl -A "react-native-#{version}" "#{tarball_url}" -Lo "#{tmp_file}" && mv "#{tmp_file}" "#{destination_path}"`
425+
return destination_path
426+
end
427+
428+
cached_path = File.join(ReactNativePodsUtils.shared_cache_dir(), filename)
429+
if File.exist?(cached_path)
430+
rncore_log("Verifying checksum for cached #{filename}...")
431+
if ReactNativePodsUtils.validate_tarball(cached_path, tarball_url)
432+
rncore_log("Cache hit: copying #{filename} from shared cache (#{ReactNativePodsUtils.shared_cache_dir()})")
433+
FileUtils.cp(cached_path, destination_path)
434+
else
435+
rncore_log("Shared cache file #{filename} failed SHA verification. Re-downloading.")
436+
File.delete(cached_path)
437+
tmp_file = "#{artifacts_dir()}/reactnative-core.download"
438+
`curl -A "react-native-#{version}" "#{tarball_url}" -Lo "#{tmp_file}" && mv "#{tmp_file}" "#{destination_path}"`
439+
rncore_log("Verifying checksum for downloaded #{filename}...")
440+
if ReactNativePodsUtils.validate_tarball(destination_path, tarball_url)
441+
FileUtils.cp(destination_path, cached_path)
442+
rncore_log("Saved #{filename} to shared cache (#{ReactNativePodsUtils.shared_cache_dir()})")
443+
else
444+
File.delete(destination_path) if File.exist?(destination_path)
445+
abort("[ReactNativeCore] Downloaded file #{filename} failed SHA verification. Aborting.")
446+
end
447+
end
418448
else
419-
rncore_log("Using downloaded ReactNativeCore-prebuilt #{dsyms ? "dSYMs " : ""}#{configuration ? configuration.to_s : ""} tarball at #{Pathname.new(destination_path).relative_path_from(Pathname.pwd).to_s}")
449+
rncore_log("Cache miss: downloading #{filename} from #{tarball_url}")
450+
# Download to a temporary file first so we don't cache incomplete downloads.
451+
tmp_file = "#{artifacts_dir()}/reactnative-core.download"
452+
`curl -A "react-native-#{version}" "#{tarball_url}" -Lo "#{tmp_file}" && mv "#{tmp_file}" "#{destination_path}"`
453+
rncore_log("Verifying checksum for downloaded #{filename}...")
454+
if ReactNativePodsUtils.validate_tarball(destination_path, tarball_url)
455+
# Save to shared cache for future use
456+
`mkdir -p "#{ReactNativePodsUtils.shared_cache_dir()}"`
457+
FileUtils.cp(destination_path, cached_path)
458+
rncore_log("Saved #{filename} to shared cache (#{ReactNativePodsUtils.shared_cache_dir()})")
459+
else
460+
File.delete(destination_path) if File.exist?(destination_path)
461+
abort("[ReactNativeCore] Downloaded file #{filename} failed SHA verification. Aborting.")
462+
end
420463
end
421464

422465
return destination_path

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

Lines changed: 51 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -232,14 +232,60 @@ def self.podspec_source_download_prebuilt_nightly_tarball(version)
232232
end
233233

234234
def self.download_rndeps_tarball(react_native_path, tarball_url, version, configuration)
235-
destination_path = configuration == nil ?
236-
"#{artifacts_dir()}/reactnative-dependencies-#{version}.tar.gz" :
237-
"#{artifacts_dir()}/reactnative-dependencies-#{version}-#{configuration}.tar.gz"
235+
filename = configuration == nil ?
236+
"reactnative-dependencies-#{version}.tar.gz" :
237+
"reactnative-dependencies-#{version}-#{configuration}.tar.gz"
238+
destination_path = "#{artifacts_dir()}/#{filename}"
239+
240+
if File.exist?(destination_path)
241+
rndeps_log("Tarball #{filename} already exists in Pods. Skipping download.")
242+
return destination_path
243+
end
244+
245+
`mkdir -p "#{artifacts_dir()}"`
238246

239-
unless File.exist?(destination_path)
247+
if ReactNativePodsUtils.skip_caches?
248+
rndeps_log("RCT_SKIP_CACHES is set. Downloading #{filename} directly (bypassing shared cache).")
249+
tmp_file = "#{artifacts_dir()}/reactnative-dependencies.download"
250+
`curl -A "react-native-#{version}" "#{tarball_url}" -Lo "#{tmp_file}" && mv "#{tmp_file}" "#{destination_path}"`
251+
return destination_path
252+
end
253+
254+
cached_path = File.join(ReactNativePodsUtils.shared_cache_dir(), filename)
255+
if File.exist?(cached_path)
256+
rndeps_log("Verifying checksum for cached #{filename}...")
257+
if ReactNativePodsUtils.validate_tarball(cached_path, tarball_url)
258+
rndeps_log("Cache hit: copying #{filename} from shared cache (#{ReactNativePodsUtils.shared_cache_dir()})")
259+
FileUtils.cp(cached_path, destination_path)
260+
else
261+
rndeps_log("Shared cache file #{filename} failed SHA verification. Re-downloading.")
262+
File.delete(cached_path)
263+
tmp_file = "#{artifacts_dir()}/reactnative-dependencies.download"
264+
`curl -A "react-native-#{version}" "#{tarball_url}" -Lo "#{tmp_file}" && mv "#{tmp_file}" "#{destination_path}"`
265+
rndeps_log("Verifying checksum for downloaded #{filename}...")
266+
if ReactNativePodsUtils.validate_tarball(destination_path, tarball_url)
267+
FileUtils.cp(destination_path, cached_path)
268+
rndeps_log("Saved #{filename} to shared cache (#{ReactNativePodsUtils.shared_cache_dir()})")
269+
else
270+
File.delete(destination_path) if File.exist?(destination_path)
271+
abort("[ReactNativeDependencies] Downloaded file #{filename} failed SHA verification. Aborting.")
272+
end
273+
end
274+
else
275+
rndeps_log("Cache miss: downloading #{filename} from #{tarball_url}")
240276
# Download to a temporary file first so we don't cache incomplete downloads.
241277
tmp_file = "#{artifacts_dir()}/reactnative-dependencies.download"
242-
`mkdir -p "#{artifacts_dir()}" && curl "#{tarball_url}" -Lo "#{tmp_file}" && mv "#{tmp_file}" "#{destination_path}"`
278+
`curl -A "react-native-#{version}" "#{tarball_url}" -Lo "#{tmp_file}" && mv "#{tmp_file}" "#{destination_path}"`
279+
rndeps_log("Verifying checksum for downloaded #{filename}...")
280+
if ReactNativePodsUtils.validate_tarball(destination_path, tarball_url)
281+
# Save to shared cache for future use
282+
`mkdir -p "#{ReactNativePodsUtils.shared_cache_dir()}"`
283+
FileUtils.cp(destination_path, cached_path)
284+
rndeps_log("Saved #{filename} to shared cache (#{ReactNativePodsUtils.shared_cache_dir()})")
285+
else
286+
File.delete(destination_path) if File.exist?(destination_path)
287+
abort("[ReactNativeDependencies] Downloaded file #{filename} failed SHA verification. Aborting.")
288+
end
243289
end
244290

245291
return destination_path

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

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
# LICENSE file in the root directory of this source tree.
55

66
require 'shellwords'
7+
require 'digest'
78

89
require_relative "./helpers.rb"
910
require_relative "./jsengine.rb"
@@ -724,4 +725,49 @@ def self.resolve_use_frameworks(spec, header_mappings_dir: nil, module_name: nil
724725
spec.header_mappings_dir = header_mappings_dir
725726
end
726727
end
728+
729+
# ==================== #
730+
# Shared download cache #
731+
# ==================== #
732+
733+
def self.skip_caches?
734+
ENV['RCT_SKIP_CACHES'] == '1'
735+
end
736+
737+
def self.shared_cache_dir()
738+
return File.join(Dir.home, "Library", "Caches", "ReactNative")
739+
end
740+
741+
def self.fetch_maven_sha1(tarball_url)
742+
sha1 = `curl -sL "#{tarball_url}.sha1"`.strip
743+
return sha1.downcase if $?.success? && sha1.match?(/\A[a-fA-F0-9]{40}\z/)
744+
nil
745+
end
746+
747+
def self.validate_tarball(path, tarball_url)
748+
expected_sha1 = fetch_maven_sha1(tarball_url)
749+
basename = File.basename(path)
750+
if expected_sha1.nil?
751+
cache_log("SHA1 not available from Maven for #{basename}. Skipping validation.")
752+
return true
753+
end
754+
actual_sha1 = Digest::SHA1.file(path).hexdigest
755+
if actual_sha1 == expected_sha1
756+
cache_log("SHA1 verified for #{basename}")
757+
return true
758+
end
759+
cache_log("SHA1 mismatch for #{basename}: expected #{expected_sha1}, got #{actual_sha1}", :error)
760+
return false
761+
end
762+
763+
def self.cache_log(message, level = :info)
764+
return unless Object.const_defined?("Pod::UI")
765+
prefix = '[Cache] '
766+
case level
767+
when :error
768+
Pod::UI.puts prefix.red + message
769+
else
770+
Pod::UI.puts prefix.green + message
771+
end
772+
end
727773
end

packages/react-native/sdks/hermes-engine/hermes-utils.rb

Lines changed: 84 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
# This source code is licensed under the MIT license found in the
44
# LICENSE file in the root directory of this source tree.
55

6+
require 'digest'
7+
68
HERMES_GITHUB_URL = "https://github.com/facebook/hermes.git"
79
ENV_BUILD_FROM_SOURCE = "RCT_BUILD_HERMES_FROM_SOURCE"
810

@@ -206,16 +208,93 @@ def download_stable_hermes(react_native_path, version, configuration)
206208
download_hermes_tarball(react_native_path, tarball_url, version, configuration)
207209
end
208210

211+
def shared_cache_dir()
212+
return File.join(Dir.home, "Library", "Caches", "ReactNative")
213+
end
214+
215+
def fetch_maven_sha1(tarball_url)
216+
sha1 = `curl -sL "#{tarball_url}.sha1"`.strip
217+
return sha1.downcase if $?.success? && sha1.match?(/\A[a-fA-F0-9]{40}\z/)
218+
nil
219+
end
220+
221+
def skip_caches?
222+
ENV['RCT_SKIP_CACHES'] == '1'
223+
end
224+
225+
def validate_hermes_tarball(path, tarball_url)
226+
expected_sha1 = fetch_maven_sha1(tarball_url)
227+
basename = File.basename(path)
228+
if expected_sha1.nil?
229+
hermes_log("SHA1 not available from Maven for #{basename}. Skipping validation.", :info)
230+
return true
231+
end
232+
actual_sha1 = Digest::SHA1.file(path).hexdigest
233+
if actual_sha1 == expected_sha1
234+
hermes_log("SHA1 verified for #{basename}", :info)
235+
return true
236+
end
237+
hermes_log("SHA1 mismatch for #{basename}: expected #{expected_sha1}, got #{actual_sha1}", :error)
238+
return false
239+
end
240+
209241
def download_hermes_tarball(react_native_path, tarball_url, version, configuration)
210-
destination_path = configuration == nil ?
211-
"#{artifacts_dir()}/hermes-ios-#{version}.tar.gz" :
212-
"#{artifacts_dir()}/hermes-ios-#{version}-#{configuration}.tar.gz"
242+
filename = configuration == nil ?
243+
"hermes-ios-#{version}.tar.gz" :
244+
"hermes-ios-#{version}-#{configuration}.tar.gz"
245+
destination_path = "#{artifacts_dir()}/#{filename}"
246+
247+
if File.exist?(destination_path)
248+
hermes_log("Tarball #{filename} already exists in Pods. Skipping download.", :info)
249+
return destination_path
250+
end
251+
252+
`mkdir -p "#{artifacts_dir()}"`
213253

214-
unless File.exist?(destination_path)
254+
if skip_caches?
255+
hermes_log("RCT_SKIP_CACHES is set. Downloading #{filename} directly (bypassing shared cache).", :info)
256+
tmp_file = "#{artifacts_dir()}/hermes-ios.download"
257+
`curl -A "react-native-#{version}" "#{tarball_url}" -Lo "#{tmp_file}" && mv "#{tmp_file}" "#{destination_path}"`
258+
return destination_path
259+
end
260+
261+
cached_path = File.join(shared_cache_dir(), filename)
262+
if File.exist?(cached_path)
263+
hermes_log("Verifying checksum for cached #{filename}...", :info)
264+
if validate_hermes_tarball(cached_path, tarball_url)
265+
hermes_log("Cache hit: copying #{filename} from shared cache (#{shared_cache_dir()})", :info)
266+
FileUtils.cp(cached_path, destination_path)
267+
else
268+
hermes_log("Shared cache file #{filename} failed SHA verification. Re-downloading.", :info)
269+
File.delete(cached_path)
270+
tmp_file = "#{artifacts_dir()}/hermes-ios.download"
271+
`curl -A "react-native-#{version}" "#{tarball_url}" -Lo "#{tmp_file}" && mv "#{tmp_file}" "#{destination_path}"`
272+
hermes_log("Verifying checksum for downloaded #{filename}...", :info)
273+
if validate_hermes_tarball(destination_path, tarball_url)
274+
FileUtils.cp(destination_path, cached_path)
275+
hermes_log("Saved #{filename} to shared cache (#{shared_cache_dir()})", :info)
276+
else
277+
File.delete(destination_path) if File.exist?(destination_path)
278+
abort("[Hermes] Downloaded file #{filename} failed SHA verification. Aborting.")
279+
end
280+
end
281+
else
282+
hermes_log("Cache miss: downloading #{filename} from #{tarball_url}", :info)
215283
# Download to a temporary file first so we don't cache incomplete downloads.
216284
tmp_file = "#{artifacts_dir()}/hermes-ios.download"
217-
`mkdir -p "#{artifacts_dir()}" && curl "#{tarball_url}" -Lo "#{tmp_file}" && mv "#{tmp_file}" "#{destination_path}"`
285+
`curl -A "react-native-#{version}" "#{tarball_url}" -Lo "#{tmp_file}" && mv "#{tmp_file}" "#{destination_path}"`
286+
hermes_log("Verifying checksum for downloaded #{filename}...", :info)
287+
if validate_hermes_tarball(destination_path, tarball_url)
288+
# Save to shared cache for future use
289+
`mkdir -p "#{shared_cache_dir()}"`
290+
FileUtils.cp(destination_path, cached_path)
291+
hermes_log("Saved #{filename} to shared cache (#{shared_cache_dir()})", :info)
292+
else
293+
File.delete(destination_path) if File.exist?(destination_path)
294+
abort("[Hermes] Downloaded file #{filename} failed SHA verification. Aborting.")
295+
end
218296
end
297+
219298
return destination_path
220299
end
221300

packages/rn-tester/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
"clean-android": "rm -rf android/app/build",
2020
"prepare-ios": "node ./cli.js bootstrap ios",
2121
"clean-ios": "rm -rf build/generated/ios Pods Podfile.lock RNTesterPods.xcworkspace/ ../react-native-codegen/lib/",
22+
"ios:clear-prebuild-caches": "rm -rf $HOME/Library/Caches/ReactNative",
2223
"e2e-build-android": "../../gradlew :packages:rn-tester:android:app:installRelease -PreactNativeArchitectures=arm64-v8a",
2324
"e2e-build-ios": "./scripts/maestro-build-ios.sh",
2425
"e2e-test-android": "maestro test .maestro/ -e APP_ID=com.facebook.react.uiapp",

0 commit comments

Comments
 (0)