diff --git a/.github/scripts/resolve-hermes.mts b/.github/scripts/resolve-hermes.mts index 8d6289573593..efff51948587 100644 --- a/.github/scripts/resolve-hermes.mts +++ b/.github/scripts/resolve-hermes.mts @@ -16,12 +16,6 @@ import { $, echo, fs, path } from 'zx'; // Use createRequire to import CommonJS modules from ESM context const require = createRequire(import.meta.url); -const { - findMatchingHermesVersion, - findVersionAtMergeBase, - getLatestStableVersionFromNPM, - hermesCommitAtMergeBase, -} = require('../../packages/react-native/scripts/ios-prebuild/microsoft-hermes.js'); const { computeNightlyTarballURL, } = require('../../packages/react-native/scripts/ios-prebuild/utils.js'); @@ -34,90 +28,126 @@ function setActionOutput(key: string, value: string) { } /** - * Downloads the upstream Hermes tarball from Maven or Sonatype. - * - * Tries multiple version resolution strategies in order: - * 1. Mapped version from peerDependencies (stable branches) - * 2. Version at merge base with facebook/react-native (main branch) - * 3. Latest stable version from npm (last resort) + * Reads the prebuilt Hermes artifact version from + * packages/react-native/sdks/hermes-engine/version.properties. * - * Returns {tarballPath, version} on success, or null if no tarball is available. + * Mirrors the upstream CocoaPods podspec: HERMES_V1_VERSION_NAME when + * RCT_HERMES_V1_ENABLED=1, otherwise HERMES_VERSION_NAME (the RN 0.83 default, + * V0). This is the Hermes version (e.g. '0.14.0') — which is how upstream keys + * the prebuilt tarballs on Maven — not the react-native version. */ -async function downloadUpstreamHermesTarball( - buildType: string = 'Debug', -): Promise<{ tarballPath: string; version: string } | null> { - const packageJsonPath = path.resolve( - import.meta.dirname!, '..', '..', 'packages', 'react-native', 'package.json', +function resolveHermesArtifactVersion(): string | null { + const propsPath = path.resolve( + import.meta.dirname!, '..', '..', + 'packages', 'react-native', 'sdks', 'hermes-engine', 'version.properties', ); - - // Build a list of candidate versions to try (in priority order) - const candidates: string[] = []; - - const mapped = findMatchingHermesVersion(packageJsonPath); - if (mapped != null) { - candidates.push(mapped); - } - - const mergeBaseVersion = findVersionAtMergeBase(); - if (mergeBaseVersion != null && !candidates.includes(mergeBaseVersion)) { - candidates.push(mergeBaseVersion); + try { + const props: Record = {}; + for (const line of fs.readFileSync(propsPath, 'utf8').split('\n')) { + const eq = line.indexOf('='); + if (eq > 0) { + props[line.slice(0, eq).trim()] = line.slice(eq + 1).trim(); + } + } + const key = + process.env.RCT_HERMES_V1_ENABLED === '1' + ? 'HERMES_V1_VERSION_NAME' + : 'HERMES_VERSION_NAME'; + const version = props[key]; + return version != null && version.length > 0 ? version : null; + } catch { + return null; } +} +/** + * Reads the pinned Hermes tag from the RN source checkout. + * + * RCT_HERMES_V1_ENABLED=1 opts into the V1 tag; otherwise use the default V0 + * tag. These tags are valid refs in facebook/hermes and can be passed directly + * to actions/checkout. + */ +function resolveHermesTag(): string | null { + const tagFile = + process.env.RCT_HERMES_V1_ENABLED === '1' + ? '.hermesv1version' + : '.hermesversion'; + const tagPath = path.resolve( + import.meta.dirname!, '..', '..', + 'packages', 'react-native', 'sdks', tagFile, + ); try { - const latestStable = await getLatestStableVersionFromNPM(); - if (!candidates.includes(latestStable)) { - candidates.push(latestStable); - } + const tag = fs.readFileSync(tagPath, 'utf8').trim(); + return tag.length > 0 ? tag : null; } catch { - // npm lookup failed, continue with what we have + return null; } +} - if (candidates.length === 0) { - echo('Could not determine any upstream version to download Hermes tarball'); +/** + * Downloads the upstream prebuilt Hermes tarball from Maven (release) or + * Sonatype snapshots (nightly). + * + * Upstream publishes prebuilt Hermes under `com/facebook/hermes/hermes-ios/`, + * keyed by the Hermes version from version.properties — matching the CocoaPods + * `release_tarball_url` in sdks/hermes-engine/hermes-utils.rb. (The fork + * previously queried `com/facebook/react/react-native-artifacts/`, + * which has no Hermes classifier, so every resolve fell back to building Hermes + * from source.) + * + * Returns {tarballPath, version} on success, or null if no tarball is available + * (callers then fall back to building Hermes from source). + */ +async function downloadUpstreamHermesTarball( + buildType: string = 'Debug', +): Promise<{ tarballPath: string; version: string } | null> { + const version = resolveHermesArtifactVersion(); + if (version == null) { + echo('Could not read Hermes version from sdks/hermes-engine/version.properties'); return null; } const mavenRepoUrl = 'https://repo1.maven.org/maven2'; - const namespace = 'com/facebook/react'; - - for (const version of candidates) { - const releaseUrl = `${mavenRepoUrl}/${namespace}/react-native-artifacts/${version}/react-native-artifacts-${version}-hermes-ios-${buildType.toLowerCase()}.tar.gz`; - const nightlyUrl = await computeNightlyTarballURL( - version, - buildType, - 'react-native-artifacts', - `hermes-ios-${buildType.toLowerCase()}.tar.gz`, - ); - const urlsToTry = [releaseUrl]; - if (nightlyUrl) { - urlsToTry.push(nightlyUrl); - } + const namespace = 'com/facebook/hermes'; + const flavor = buildType.toLowerCase(); - for (const tarballUrl of urlsToTry) { - echo(`Trying upstream Hermes tarball (version: ${version}, ${buildType}) at ${tarballUrl}...`); - - try { - const response = await fetch(tarballUrl); - if (!response.ok) { - echo(`Tarball not available: ${response.status} ${response.statusText}`); - continue; - } - - const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'hermes-')); - const tarballPath = path.join(tmpDir, 'hermes-ios.tar.gz'); - const buffer = await response.arrayBuffer(); - fs.writeFileSync(tarballPath, Buffer.from(buffer)); - - echo(`Downloaded upstream Hermes tarball (${version}) to ${tarballPath}`); - return { tarballPath, version }; - } catch (e: any) { - echo(`Error downloading tarball for ${version}: ${e.message}`); + const releaseUrl = `${mavenRepoUrl}/${namespace}/hermes-ios/${version}/hermes-ios-${version}-hermes-ios-${flavor}.tar.gz`; + const nightlyUrl = await computeNightlyTarballURL( + version, + buildType, + 'hermes-ios', + `hermes-ios-${flavor}.tar.gz`, + ); + + const urlsToTry = [releaseUrl]; + if (nightlyUrl) { + urlsToTry.push(nightlyUrl); + } + + for (const tarballUrl of urlsToTry) { + echo(`Trying upstream Hermes tarball (version: ${version}, ${buildType}) at ${tarballUrl}...`); + + try { + const response = await fetch(tarballUrl); + if (!response.ok) { + echo(`Tarball not available: ${response.status} ${response.statusText}`); continue; } + + const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'hermes-')); + const tarballPath = path.join(tmpDir, 'hermes-ios.tar.gz'); + const buffer = await response.arrayBuffer(); + fs.writeFileSync(tarballPath, Buffer.from(buffer)); + + echo(`Downloaded upstream Hermes tarball (${version}) to ${tarballPath}`); + return { tarballPath, version }; + } catch (e: any) { + echo(`Error downloading tarball for ${version}: ${e.message}`); + continue; } } - echo('No upstream Hermes tarball found for any candidate version — will build from source.'); + echo('No upstream Hermes tarball found — will build from source.'); return null; } @@ -125,10 +155,11 @@ async function downloadUpstreamHermesTarball( * Extracts an upstream Hermes tarball and recomposes the xcframework to include * the macOS slice, if needed. * - * Upstream tarballs ship a universal xcframework (iOS, simulator, catalyst, - * tvOS, visionOS) plus a standalone macosx/hermes.framework. This function - * merges the standalone macOS framework into the universal xcframework using - * `xcodebuild -create-xcframework`. + * As of RN 0.83, upstream renamed the Hermes binary product from `hermes` to + * `hermesvm`. Upstream tarballs ship a universal `hermesvm.xcframework` (iOS, + * simulator, catalyst, tvOS, visionOS) plus a standalone `macosx/hermesvm.framework`. + * This function merges the standalone macOS framework into the universal + * xcframework using `xcodebuild -create-xcframework`. * * NOTE: Once upstream Hermes includes macOS in the universal xcframework * natively, this function will detect the existing macOS slice and skip @@ -147,7 +178,7 @@ async function recomposeHermesXcframework( await $`tar -xzf ${tarballPath} -C ${destroot} --strip-components=2`; const frameworksDir = path.join(destroot, 'Library', 'Frameworks'); - const xcfwPath = path.join(frameworksDir, 'universal', 'hermes.xcframework'); + const xcfwPath = path.join(frameworksDir, 'universal', 'hermesvm.xcframework'); echo('Upstream tarball contents:'); await $`ls -la ${frameworksDir}`; @@ -167,16 +198,16 @@ async function recomposeHermesXcframework( } // Check for standalone macOS framework - const standaloneMacFw = path.join(frameworksDir, 'macosx', 'hermes.framework'); + const standaloneMacFw = path.join(frameworksDir, 'macosx', 'hermesvm.framework'); if (!fs.existsSync(standaloneMacFw)) { - echo('ERROR: Upstream tarball missing macosx/hermes.framework'); + echo('ERROR: Upstream tarball missing macosx/hermesvm.framework'); return false; } // Collect existing frameworks from inside the universal xcframework const frameworkArgs: string[] = []; for (const entry of xcfwContents) { - const fwPath = path.join(xcfwPath, entry, 'hermes.framework'); + const fwPath = path.join(xcfwPath, entry, 'hermesvm.framework'); if (fs.existsSync(fwPath) && fs.statSync(fwPath).isDirectory()) { echo(`Found slice: ${fwPath}`); frameworkArgs.push('-framework', fwPath); @@ -188,7 +219,7 @@ async function recomposeHermesXcframework( frameworkArgs.push('-framework', standaloneMacFw); // Build new xcframework at a temp path (frameworks reference paths inside the old xcfw) - const xcfwNew = path.join(frameworksDir, 'universal', 'hermes-new.xcframework'); + const xcfwNew = path.join(frameworksDir, 'universal', 'hermesvm-new.xcframework'); const sliceCount = frameworkArgs.filter(f => f !== '-framework').length; echo(`Creating new universal xcframework with ${sliceCount} slices...`); await $`xcodebuild -create-xcframework ${frameworkArgs} -output ${xcfwNew} -allow-internal-distribution`; @@ -239,9 +270,13 @@ switch (command) { break; } case 'resolve-commit': { - const { commit } = hermesCommitAtMergeBase(); - setActionOutput('hermes-commit', commit); - echo(`Resolved Hermes commit: ${commit}`); + const tag = resolveHermesTag(); + if (tag == null) { + echo('Could not read pinned Hermes tag from sdks/.hermesversion or sdks/.hermesv1version'); + process.exit(1); + } + setActionOutput('hermes-commit', tag); + echo(`Resolved Hermes tag: ${tag}`); break; } default: diff --git a/.github/workflows/microsoft-resolve-hermes.yml b/.github/workflows/microsoft-resolve-hermes.yml index 78f590715ddc..ab153d6e8290 100644 --- a/.github/workflows/microsoft-resolve-hermes.yml +++ b/.github/workflows/microsoft-resolve-hermes.yml @@ -3,7 +3,8 @@ # Strategy (fast path first): # 1. Download upstream Hermes tarball from Maven/Sonatype # 2. If found → recompose xcframework (add macOS slice) → upload artifact → done -# 3. If not found → resolve Hermes commit at merge base → check cache → upload if cached +# 3. If not found → resolve pinned Hermes tag from sdks/.hermesversion or +# sdks/.hermesv1version → check cache → upload if cached # # Build-from-source fallback (only when recomposed != true AND cache-hit != true): # build-hermesc → build 5 platform slices in parallel → assemble universal xcframework @@ -67,9 +68,9 @@ jobs: path: hermes-destroot retention-days: 30 - # Step 3 (fallback): No upstream tarball — resolve the Hermes commit hash - # at the merge base with facebook/react-native and check the build cache. - - name: Resolve Hermes commit at merge base + # Step 3 (fallback): No upstream tarball — resolve the pinned Hermes tag + # from sdks/.hermesversion or sdks/.hermesv1version and check the build cache. + - name: Resolve pinned Hermes tag if: steps.recompose.outputs.recomposed != 'true' id: resolve run: node .github/scripts/resolve-hermes.mts resolve-commit diff --git a/packages/react-native/scripts/ios-prebuild/hermes.js b/packages/react-native/scripts/ios-prebuild/hermes.js index dcc3e47c3791..72c0c4c2073a 100644 --- a/packages/react-native/scripts/ios-prebuild/hermes.js +++ b/packages/react-native/scripts/ios-prebuild/hermes.js @@ -8,14 +8,9 @@ * @format */ -const { - findMatchingHermesVersion, - hermesCommitAtMergeBase, -} = require('./microsoft-hermes'); // [macOS] const {computeNightlyTarballURL, createLogger} = require('./utils'); const {execSync} = require('child_process'); const fs = require('fs'); -const os = require('os'); // [macOS] const path = require('path'); const stream = require('stream'); const {promisify} = require('util'); @@ -61,27 +56,6 @@ async function prepareHermesArtifactsAsync( // Resolve the version from the environment variable or use the default version let resolvedVersion = process.env.HERMES_VERSION ?? 'nightly'; - // [macOS] Map macOS version to upstream RN version for artifact lookup. - let allowBuildFromSource = false; - if (!process.env.HERMES_VERSION) { - const packageJsonPath = path.resolve( - __dirname, - '..', - '..', - 'package.json', - ); - const mappedVersion = findMatchingHermesVersion(packageJsonPath); - if (mappedVersion != null) { - hermesLog( - `Using mapped upstream version for Hermes lookup: ${mappedVersion}`, - ); - resolvedVersion = mappedVersion; - } else { - allowBuildFromSource = true; - } - } - // macOS] - if (resolvedVersion === 'nightly') { hermesLog('Using latest nightly tarball'); const hermesVersion = await getNightlyVersionFromNPM(); @@ -100,11 +74,7 @@ async function prepareHermesArtifactsAsync( return artifactsPath; } - const sourceType = await hermesSourceType( - resolvedVersion, - buildType, - allowBuildFromSource, // [macOS] - ); + const sourceType = await hermesSourceType(resolvedVersion, buildType); localPath = await resolveSourceFromSourceType( sourceType, resolvedVersion, @@ -154,19 +124,16 @@ type HermesEngineSourceType = | 'local_prebuilt_tarball' | 'download_prebuild_tarball' | 'download_prebuilt_nightly_tarball' - | 'build_from_hermes_commit' // [macOS] */ const HermesEngineSourceTypes /*:{ +DOWNLOAD_PREBUILD_TARBALL: "download_prebuild_tarball", +DOWNLOAD_PREBUILT_NIGHTLY_TARBALL: "download_prebuilt_nightly_tarball", - +LOCAL_PREBUILT_TARBALL: "local_prebuilt_tarball", - +BUILD_FROM_HERMES_COMMIT: "build_from_hermes_commit" // [macOS] + +LOCAL_PREBUILT_TARBALL: "local_prebuilt_tarball" } */ = { LOCAL_PREBUILT_TARBALL: 'local_prebuilt_tarball', DOWNLOAD_PREBUILD_TARBALL: 'download_prebuild_tarball', DOWNLOAD_PREBUILT_NIGHTLY_TARBALL: 'download_prebuilt_nightly_tarball', - BUILD_FROM_HERMES_COMMIT: 'build_from_hermes_commit', // [macOS] }; /** @@ -265,7 +232,6 @@ async function hermesArtifactExists( async function hermesSourceType( version /*: string */, buildType /*: BuildFlavor */, - allowBuildFromSource /*: boolean */ = false, // [macOS] ) /*: Promise */ { if (hermesEngineTarballEnvvarDefined()) { hermesLog('Using local prebuild tarball'); @@ -285,15 +251,6 @@ async function hermesSourceType( return HermesEngineSourceTypes.DOWNLOAD_PREBUILT_NIGHTLY_TARBALL; } - // [macOS] Fall back to building Hermes from the merge-base commit. - if (allowBuildFromSource) { - hermesLog( - 'No prebuilt Hermes artifact found. Will attempt to resolve from merge base with facebook/react-native.', - ); - return HermesEngineSourceTypes.BUILD_FROM_HERMES_COMMIT; - } - // macOS] - hermesLog( 'Using download prebuild nightly tarball - this is a fallback and might not work.', ); @@ -313,8 +270,6 @@ async function resolveSourceFromSourceType( return downloadPrebuildTarball(version, buildType, artifactsPath); case HermesEngineSourceTypes.DOWNLOAD_PREBUILT_NIGHTLY_TARBALL: return downloadPrebuiltNightlyTarball(version, buildType, artifactsPath); - case HermesEngineSourceTypes.BUILD_FROM_HERMES_COMMIT: // [macOS] - return buildFromHermesCommit(version, buildType, artifactsPath); default: abort( `[Hermes] Unsupported or invalid source type provided: ${sourceType}`, @@ -421,113 +376,6 @@ async function downloadHermesTarball( return destPath; } -// [macOS -/** - * Handles the case where no prebuilt Hermes artifacts are available. - * Determines the Hermes commit at the merge base with facebook/react-native - * and provides actionable guidance for building Hermes. - */ -async function buildFromHermesCommit( - version /*: string */, - buildType /*: BuildFlavor */, - artifactsPath /*: string */, -) /*: Promise */ { - const {commit, timestamp} = hermesCommitAtMergeBase(); - hermesLog( - `Building Hermes from source at commit ${commit} (merge base timestamp: ${timestamp})`, - ); - - const HERMES_GITHUB_URL = 'https://github.com/facebook/hermes.git'; - const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'hermes-build-')); - const hermesDir = path.join(tmpDir, 'hermes'); - - try { - // Clone Hermes at the identified commit using the most efficient - // single-fetch pattern (see https://github.com/actions/checkout) - hermesLog(`Cloning Hermes at commit ${commit}...`); - execSync(`git init "${hermesDir}"`, {stdio: 'inherit'}); - execSync(`git -C "${hermesDir}" remote add origin ${HERMES_GITHUB_URL}`, { - stdio: 'inherit', - }); - execSync( - `git -C "${hermesDir}" fetch --no-tags --depth 1 origin +${commit}:refs/remotes/origin/main`, - {stdio: 'inherit', timeout: 300000}, - ); - execSync(`git -C "${hermesDir}" checkout main`, {stdio: 'inherit'}); - - const reactNativeRoot = path.resolve(__dirname, '..', '..'); - const buildScript = path.join( - reactNativeRoot, - 'sdks', - 'hermes-engine', - 'utils', - 'build-ios-framework.sh', - ); - - const buildEnv = { - ...process.env, - BUILD_TYPE: buildType, - HERMES_PATH: hermesDir, - JSI_PATH: path.join(hermesDir, 'API', 'jsi'), - REACT_NATIVE_PATH: reactNativeRoot, - // Deployment targets matching react-native-macos minimums - IOS_DEPLOYMENT_TARGET: '15.1', - MAC_DEPLOYMENT_TARGET: '14.0', - XROS_DEPLOYMENT_TARGET: '1.0', - RELEASE_VERSION: version, - }; - - hermesLog(`Building Hermes frameworks (${buildType})...`); - execSync(`bash "${buildScript}"`, { - stdio: 'inherit', - cwd: hermesDir, - timeout: 3600000, // 60 minutes - env: buildEnv, - }); - - // Create tarball from the destroot (same structure as Maven artifacts) - const tarballName = `hermes-ios-${buildType.toLowerCase()}.tar.gz`; - const tarballPath = path.join(artifactsPath, tarballName); - hermesLog('Creating Hermes tarball from build output...'); - execSync(`tar -czf "${tarballPath}" -C "${hermesDir}" destroot`, { - stdio: 'inherit', - }); - - hermesLog(`Hermes built from source and packaged at ${tarballPath}`); - return tarballPath; - } catch (e) { - // Dump CMake error logs before cleanup for debugging - try { - const cmakeErrorLog = path.join( - hermesDir, - 'build_host_hermesc', - 'CMakeFiles', - 'CMakeError.log', - ); - if (fs.existsSync(cmakeErrorLog)) { - hermesLog('=== CMakeError.log ==='); - hermesLog(fs.readFileSync(cmakeErrorLog, 'utf8')); - } - } catch (_) { - // ignore - } - - abort( - `[Hermes] Failed to build Hermes from source at commit ${commit}.\n` + - `Error: ${e.message}\n` + - `To resolve, either:\n` + - ` 1. Set HERMES_ENGINE_TARBALL_PATH to a local Hermes tarball path\n` + - ` 2. Set HERMES_VERSION to an upstream RN version with published artifacts\n` + - ` 3. Build Hermes manually from commit ${commit} and provide the tarball path via HERMES_ENGINE_TARBALL_PATH`, - ); - return ''; // unreachable - } finally { - // Clean up - fs.rmSync(tmpDir, {recursive: true, force: true}); - } -} -// macOS] - function abort(message /*: string */) { hermesLog(message, 'error'); throw new Error(message); diff --git a/packages/react-native/scripts/ios-prebuild/microsoft-hermes.js b/packages/react-native/scripts/ios-prebuild/microsoft-hermes.js index 9bdeec72d8fc..968b8b2aafe2 100644 --- a/packages/react-native/scripts/ios-prebuild/microsoft-hermes.js +++ b/packages/react-native/scripts/ios-prebuild/microsoft-hermes.js @@ -4,11 +4,8 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * [macOS] Resolves Hermes artifacts for macOS fork branches. - * - * Library functions for version resolution and resolving Hermes commits. - * The CI entry point that orchestrates downloading, recomposing, and - * caching is at .github/scripts/resolve-hermes.mts. + * [macOS] Library functions for resolving React Native versions used by + * Hermes-related dependency artifacts. * * @flow * @format @@ -17,8 +14,6 @@ const {createLogger} = require('./utils'); const {execSync} = require('child_process'); const fs = require('fs'); -const os = require('os'); -const path = require('path'); const macosLog = createLogger('macOS'); @@ -56,88 +51,6 @@ function findMatchingHermesVersion( return null; } -/** - * Finds the Hermes commit at the merge base with facebook/react-native. - * Used on the main branch (1000.0.0) where no prebuilt artifacts exist. - * - * Since react-native-macos lags slightly behind facebook/react-native, we can't always use - * the latest Hermes commit because Hermes and JSI don't always guarantee backwards compatibility. - * Instead, we take the commit hash of Hermes at the time of the merge base with facebook/react-native. - * - * This is the JavaScript equivalent of the Ruby `hermes_commit_at_merge_base` - * in sdks/hermes-engine/hermes-utils.rb. - */ -function hermesCommitAtMergeBase() /*: {| commit: string, timestamp: string |} */ { - const HERMES_GITHUB_URL = 'https://github.com/facebook/hermes.git'; - - // Fetch upstream react-native - macosLog('Fetching facebook/react-native to find merge base...'); - try { - execSync('git fetch -q https://github.com/facebook/react-native.git', { - stdio: 'pipe', - }); - } catch (e) { - abort( - '[Hermes] Failed to fetch facebook/react-native into the local repository.', - ); - } - - // Find merge base between our HEAD and upstream's HEAD - const mergeBase = execSync('git merge-base FETCH_HEAD HEAD', { - encoding: 'utf8', - }).trim(); - if (!mergeBase) { - abort( - "[Hermes] Unable to find the merge base between our HEAD and upstream's HEAD.", - ); - } - - // Get timestamp of merge base - const timestamp = execSync(`git show -s --format=%ci ${mergeBase}`, { - encoding: 'utf8', - }).trim(); - if (!timestamp) { - abort( - `[Hermes] Unable to extract the timestamp for the merge base (${mergeBase}).`, - ); - } - - // Clone Hermes bare (minimal) into a temp directory and find the commit - macosLog( - `Merge base timestamp: ${timestamp}. Cloning Hermes to find matching commit...`, - ); - const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'hermes-')); - const hermesGitDir = path.join(tmpDir, 'hermes.git'); - - try { - // Explicitly use Hermes 'main' branch since the default branch changed to 'static_h' (Hermes V1) - execSync( - `git clone -q --bare --filter=blob:none --single-branch --branch main ${HERMES_GITHUB_URL} "${hermesGitDir}"`, - {stdio: 'pipe', timeout: 120000}, - ); - - // Find the Hermes commit at the time of the merge base on branch 'main' - const commit = execSync( - `git --git-dir="${hermesGitDir}" rev-list -1 --before="${timestamp}" refs/heads/main`, - {encoding: 'utf8'}, - ).trim(); - - if (!commit) { - abort( - `[Hermes] Unable to find the Hermes commit hash at time ${timestamp} on branch 'main'.`, - ); - } - - macosLog( - `Using Hermes commit from the merge base with facebook/react-native: ${commit} (timestamp: ${timestamp})`, - ); - return {commit, timestamp}; - } finally { - // Clean up temp directory - fs.rmSync(tmpDir, {recursive: true, force: true}); - } -} - /** * Finds the upstream react-native version at the merge base with facebook/react-native. * Falls back to null if the version at merge base is also 1000.0.0 (i.e. merge base is @@ -145,8 +58,8 @@ function hermesCommitAtMergeBase() /*: {| commit: string, timestamp: string |} * */ function findVersionAtMergeBase() /*: ?string */ { try { - // hermesCommitAtMergeBase() already fetches facebook/react-native, but we - // might not have FETCH_HEAD if this runs standalone. Fetch it. + // Ensure facebook/react-native is fetched so FETCH_HEAD is available when + // this runs standalone. execSync('git fetch -q https://github.com/facebook/react-native.git', { stdio: 'pipe', timeout: 60000, @@ -188,14 +101,8 @@ async function getLatestStableVersionFromNPM() /*: Promise */ { return json.version; } -function abort(message /*: string */) { - macosLog(message, 'error'); - throw new Error(message); -} - module.exports = { findMatchingHermesVersion, - hermesCommitAtMergeBase, findVersionAtMergeBase, getLatestStableVersionFromNPM, }; diff --git a/packages/react-native/sdks/hermes-engine/hermes-utils.rb b/packages/react-native/sdks/hermes-engine/hermes-utils.rb index 2118dddea813..0686a7532c5d 100644 --- a/packages/react-native/sdks/hermes-engine/hermes-utils.rb +++ b/packages/react-native/sdks/hermes-engine/hermes-utils.rb @@ -5,9 +5,6 @@ require 'net/http' require 'rexml/document' -require 'open3' # [macOS] -require 'json' # [macOS] -require 'tmpdir' # [macOS] HERMES_GITHUB_URL = "https://github.com/facebook/hermes.git" ENV_BUILD_FROM_SOURCE = "RCT_BUILD_HERMES_FROM_SOURCE" @@ -187,21 +184,9 @@ def podspec_source_build_from_github_tag(react_native_path) end def podspec_source_build_from_github_main() - # branch = hermes_v1_enabled() ? "250829098.0.0-stable" : "main" - # hermes_log("Using the latest commit from #{branch}.") - # return {:git => HERMES_GITHUB_URL, :commit => `git ls-remote #{HERMES_GITHUB_URL} #{branch} | cut -f 1`.strip} - - # [macOS - # The logic for this is a bit different on macOS. - # Since react-native-macos lags slightly behind facebook/react-native, we can't always use - # the latest Hermes commit because Hermes and JSI don't always guarantee backwards compatibility. - # Instead, we take the commit hash of Hermes at the time of the merge base with facebook/react-native. - tuple = hermes_commit_at_merge_base() - commit = tuple[:commit] - timestamp = tuple[:timestamp] - hermes_log("Using Hermes commit from the merge base with facebook/react-native: #{commit} and timestamp: #{timestamp}") - return {:git => HERMES_GITHUB_URL, :commit => commit} - # macOS] + branch = hermes_v1_enabled() ? "250829098.0.0-stable" : "main" + hermes_log("Using the latest commit from #{branch}.") + return {:git => HERMES_GITHUB_URL, :commit => `git ls-remote #{HERMES_GITHUB_URL} #{branch} | cut -f 1`.strip} end def podspec_source_download_prebuild_release_tarball(react_native_path, version) @@ -224,49 +209,6 @@ def artifacts_dir() return File.join(Pod::Config.instance.project_pods_root, "hermes-engine-artifacts") end -# [macOS -def hermes_commit_at_merge_base() - # We don't need ls-remote because react-native-macos is a fork of facebook/react-native - fetch_result = `git fetch -q https://github.com/facebook/react-native.git` - if $?.exitstatus != 0 - abort <<-EOS - [Hermes] Failed to fetch facebook/react-native into the local repository. - EOS - end - - merge_base = `git merge-base FETCH_HEAD HEAD`.strip - if merge_base.empty? - abort <<-EOS - [Hermes] Unable to find the merge base between our HEAD and upstream's HEAD. - EOS - end - - timestamp = `git show -s --format=%ci #{merge_base}`.strip - if timestamp.empty? - abort <<-EOS - [Hermes] Unable to extract the timestamp for the merge base (#{merge_base}). - EOS - end - - commit = nil - Dir.mktmpdir do |tmpdir| - hermes_git_dir = File.join(tmpdir, "hermes.git") - # Explicitly use Hermes 'main' branch since the default branch changed to 'static_h' (Hermes V1) - `git clone -q --bare --filter=blob:none --single-branch --branch main #{HERMES_GITHUB_URL} "#{hermes_git_dir}"` - - # If all goes well, this will be the commit hash of Hermes at the time of the merge base on branch 'main' - commit = `git --git-dir="#{hermes_git_dir}" rev-list -1 --before="#{timestamp}" refs/heads/main`.strip - if commit.empty? - abort <<-EOS - [Hermes] Unable to find the Hermes commit hash at time #{timestamp} on branch 'main'. - EOS - end - end - - return { :commit => commit, :timestamp => timestamp} -end -# macOS] - def hermestag_file(react_native_path) if hermes_v1_enabled() return File.join(react_native_path, "sdks", ".hermesv1version")