Skip to content

Commit dec524b

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 82536f2 commit dec524b

4 files changed

Lines changed: 203 additions & 18 deletions

File tree

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

Lines changed: 44 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -406,17 +406,53 @@ 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)
419+
`mkdir -p "#{artifacts_dir()}"`
420+
421+
cached_path = File.join(ReactNativePodsUtils.shared_cache_dir(), filename)
422+
if File.exist?(cached_path)
423+
rncore_log("Verifying checksum for cached #{filename}...")
424+
if ReactNativePodsUtils.validate_tarball(cached_path, tarball_url)
425+
rncore_log("Cache hit: copying #{filename} from shared cache (#{ReactNativePodsUtils.shared_cache_dir()})")
426+
FileUtils.cp(cached_path, destination_path)
427+
else
428+
rncore_log("Shared cache file #{filename} failed SHA verification. Re-downloading.")
429+
File.delete(cached_path)
430+
tmp_file = "#{artifacts_dir()}/reactnative-core.download"
431+
`curl "#{tarball_url}" -Lo "#{tmp_file}" && mv "#{tmp_file}" "#{destination_path}"`
432+
rncore_log("Verifying checksum for downloaded #{filename}...")
433+
if ReactNativePodsUtils.validate_tarball(destination_path, tarball_url)
434+
FileUtils.cp(destination_path, cached_path)
435+
rncore_log("Saved #{filename} to shared cache (#{ReactNativePodsUtils.shared_cache_dir()})")
436+
else
437+
File.delete(destination_path) if File.exist?(destination_path)
438+
abort("[ReactNativeCore] Downloaded file #{filename} failed SHA verification. Aborting.")
439+
end
440+
end
441+
else
442+
rncore_log("Cache miss: downloading #{filename} from #{tarball_url}")
414443
# 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}")
416444
tmp_file = "#{artifacts_dir()}/reactnative-core.download"
417-
`mkdir -p "#{artifacts_dir()}" && curl "#{tarball_url}" -Lo "#{tmp_file}" && mv "#{tmp_file}" "#{destination_path}"`
418-
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}")
445+
`curl "#{tarball_url}" -Lo "#{tmp_file}" && mv "#{tmp_file}" "#{destination_path}"`
446+
rncore_log("Verifying checksum for downloaded #{filename}...")
447+
if ReactNativePodsUtils.validate_tarball(destination_path, tarball_url)
448+
# Save to shared cache for future use
449+
`mkdir -p "#{ReactNativePodsUtils.shared_cache_dir()}"`
450+
FileUtils.cp(destination_path, cached_path)
451+
rncore_log("Saved #{filename} to shared cache (#{ReactNativePodsUtils.shared_cache_dir()})")
452+
else
453+
File.delete(destination_path) if File.exist?(destination_path)
454+
abort("[ReactNativeCore] Downloaded file #{filename} failed SHA verification. Aborting.")
455+
end
420456
end
421457

422458
return destination_path

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

Lines changed: 44 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -232,14 +232,53 @@ 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
238244

239-
unless File.exist?(destination_path)
245+
`mkdir -p "#{artifacts_dir()}"`
246+
247+
cached_path = File.join(ReactNativePodsUtils.shared_cache_dir(), filename)
248+
if File.exist?(cached_path)
249+
rndeps_log("Verifying checksum for cached #{filename}...")
250+
if ReactNativePodsUtils.validate_tarball(cached_path, tarball_url)
251+
rndeps_log("Cache hit: copying #{filename} from shared cache (#{ReactNativePodsUtils.shared_cache_dir()})")
252+
FileUtils.cp(cached_path, destination_path)
253+
else
254+
rndeps_log("Shared cache file #{filename} failed SHA verification. Re-downloading.")
255+
File.delete(cached_path)
256+
tmp_file = "#{artifacts_dir()}/reactnative-dependencies.download"
257+
`curl "#{tarball_url}" -Lo "#{tmp_file}" && mv "#{tmp_file}" "#{destination_path}"`
258+
rndeps_log("Verifying checksum for downloaded #{filename}...")
259+
if ReactNativePodsUtils.validate_tarball(destination_path, tarball_url)
260+
FileUtils.cp(destination_path, cached_path)
261+
rndeps_log("Saved #{filename} to shared cache (#{ReactNativePodsUtils.shared_cache_dir()})")
262+
else
263+
File.delete(destination_path) if File.exist?(destination_path)
264+
abort("[ReactNativeDependencies] Downloaded file #{filename} failed SHA verification. Aborting.")
265+
end
266+
end
267+
else
268+
rndeps_log("Cache miss: downloading #{filename} from #{tarball_url}")
240269
# Download to a temporary file first so we don't cache incomplete downloads.
241270
tmp_file = "#{artifacts_dir()}/reactnative-dependencies.download"
242-
`mkdir -p "#{artifacts_dir()}" && curl "#{tarball_url}" -Lo "#{tmp_file}" && mv "#{tmp_file}" "#{destination_path}"`
271+
`curl "#{tarball_url}" -Lo "#{tmp_file}" && mv "#{tmp_file}" "#{destination_path}"`
272+
rndeps_log("Verifying checksum for downloaded #{filename}...")
273+
if ReactNativePodsUtils.validate_tarball(destination_path, tarball_url)
274+
# Save to shared cache for future use
275+
`mkdir -p "#{ReactNativePodsUtils.shared_cache_dir()}"`
276+
FileUtils.cp(destination_path, cached_path)
277+
rndeps_log("Saved #{filename} to shared cache (#{ReactNativePodsUtils.shared_cache_dir()})")
278+
else
279+
File.delete(destination_path) if File.exist?(destination_path)
280+
abort("[ReactNativeDependencies] Downloaded file #{filename} failed SHA verification. Aborting.")
281+
end
243282
end
244283

245284
return destination_path

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

Lines changed: 42 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,45 @@ 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.shared_cache_dir()
734+
return File.join(Dir.home, "Library", "Caches", "ReactNative")
735+
end
736+
737+
def self.fetch_maven_sha1(tarball_url)
738+
sha1 = `curl -sL "#{tarball_url}.sha1"`.strip
739+
return sha1.downcase if $?.success? && sha1.match?(/\A[a-fA-F0-9]{40}\z/)
740+
nil
741+
end
742+
743+
def self.validate_tarball(path, tarball_url)
744+
expected_sha1 = fetch_maven_sha1(tarball_url)
745+
basename = File.basename(path)
746+
if expected_sha1.nil?
747+
cache_log("SHA1 not available from Maven for #{basename}. Skipping validation.")
748+
return true
749+
end
750+
actual_sha1 = Digest::SHA1.file(path).hexdigest
751+
if actual_sha1 == expected_sha1
752+
cache_log("SHA1 verified for #{basename}")
753+
return true
754+
end
755+
cache_log("SHA1 mismatch for #{basename}: expected #{expected_sha1}, got #{actual_sha1}", :error)
756+
return false
757+
end
758+
759+
def self.cache_log(message, level = :info)
760+
return unless Object.const_defined?("Pod::UI")
761+
prefix = '[Cache] '
762+
case level
763+
when :error
764+
Pod::UI.puts prefix.red + message
765+
else
766+
Pod::UI.puts prefix.green + message
767+
end
768+
end
727769
end

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

Lines changed: 73 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,82 @@ 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 validate_hermes_tarball(path, tarball_url)
222+
expected_sha1 = fetch_maven_sha1(tarball_url)
223+
basename = File.basename(path)
224+
if expected_sha1.nil?
225+
hermes_log("SHA1 not available from Maven for #{basename}. Skipping validation.", :info)
226+
return true
227+
end
228+
actual_sha1 = Digest::SHA1.file(path).hexdigest
229+
if actual_sha1 == expected_sha1
230+
hermes_log("SHA1 verified for #{basename}", :info)
231+
return true
232+
end
233+
hermes_log("SHA1 mismatch for #{basename}: expected #{expected_sha1}, got #{actual_sha1}", :error)
234+
return false
235+
end
236+
209237
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"
238+
filename = configuration == nil ?
239+
"hermes-ios-#{version}.tar.gz" :
240+
"hermes-ios-#{version}-#{configuration}.tar.gz"
241+
destination_path = "#{artifacts_dir()}/#{filename}"
242+
243+
if File.exist?(destination_path)
244+
hermes_log("Tarball #{filename} already exists in Pods. Skipping download.", :info)
245+
return destination_path
246+
end
213247

214-
unless File.exist?(destination_path)
248+
`mkdir -p "#{artifacts_dir()}"`
249+
250+
cached_path = File.join(shared_cache_dir(), filename)
251+
if File.exist?(cached_path)
252+
hermes_log("Verifying checksum for cached #{filename}...", :info)
253+
if validate_hermes_tarball(cached_path, tarball_url)
254+
hermes_log("Cache hit: copying #{filename} from shared cache (#{shared_cache_dir()})", :info)
255+
FileUtils.cp(cached_path, destination_path)
256+
else
257+
hermes_log("Shared cache file #{filename} failed SHA verification. Re-downloading.", :info)
258+
File.delete(cached_path)
259+
tmp_file = "#{artifacts_dir()}/hermes-ios.download"
260+
`curl "#{tarball_url}" -Lo "#{tmp_file}" && mv "#{tmp_file}" "#{destination_path}"`
261+
hermes_log("Verifying checksum for downloaded #{filename}...", :info)
262+
if validate_hermes_tarball(destination_path, tarball_url)
263+
FileUtils.cp(destination_path, cached_path)
264+
hermes_log("Saved #{filename} to shared cache (#{shared_cache_dir()})", :info)
265+
else
266+
File.delete(destination_path) if File.exist?(destination_path)
267+
abort("[Hermes] Downloaded file #{filename} failed SHA verification. Aborting.")
268+
end
269+
end
270+
else
271+
hermes_log("Cache miss: downloading #{filename} from #{tarball_url}", :info)
215272
# Download to a temporary file first so we don't cache incomplete downloads.
216273
tmp_file = "#{artifacts_dir()}/hermes-ios.download"
217-
`mkdir -p "#{artifacts_dir()}" && curl "#{tarball_url}" -Lo "#{tmp_file}" && mv "#{tmp_file}" "#{destination_path}"`
274+
`curl "#{tarball_url}" -Lo "#{tmp_file}" && mv "#{tmp_file}" "#{destination_path}"`
275+
hermes_log("Verifying checksum for downloaded #{filename}...", :info)
276+
if validate_hermes_tarball(destination_path, tarball_url)
277+
# Save to shared cache for future use
278+
`mkdir -p "#{shared_cache_dir()}"`
279+
FileUtils.cp(destination_path, cached_path)
280+
hermes_log("Saved #{filename} to shared cache (#{shared_cache_dir()})", :info)
281+
else
282+
File.delete(destination_path) if File.exist?(destination_path)
283+
abort("[Hermes] Downloaded file #{filename} failed SHA verification. Aborting.")
284+
end
218285
end
286+
219287
return destination_path
220288
end
221289

0 commit comments

Comments
 (0)