mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/52514 As per title, this change add the `ENTERPRISE_REPOSITORY` env variable so that users can use their owm maven mirror to consume artifacts rather than the official url. This is helpful as: - we can reduce the traffic toward maven central - companies can speed up their builds by relying on local/closer replicas ## Changelog: [iOS][Added] - Add the `ENTERPRISE_REPOSITORY` env variable to cocopaods infra Reviewed By: cortinico Differential Revision: D78011424 fbshipit-source-id: 24b83b4866095d7fe3318362afc5075b99b146e7
375 lines
12 KiB
JavaScript
375 lines
12 KiB
JavaScript
/**
|
|
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
*
|
|
* This source code is licensed under the MIT license found in the
|
|
* LICENSE file in the root directory of this source tree.
|
|
*
|
|
* @flow
|
|
* @format
|
|
*/
|
|
|
|
/*:: import type {BuildFlavor} from './types'; */
|
|
|
|
const {computeNightlyTarballURL, createLogger} = require('./utils');
|
|
const {execSync} = require('child_process');
|
|
const fs = require('fs');
|
|
const path = require('path');
|
|
const stream = require('stream');
|
|
const {promisify} = require('util');
|
|
|
|
const pipeline = promisify(stream.pipeline);
|
|
|
|
const dependencyLog = createLogger('ReactNativeDependencies');
|
|
|
|
/**
|
|
* Downloads ReactNativeDependencies artifacts from the specified version and build type. If you want to specify a specific
|
|
* version of ReactNativeDependencies, use the RN_DEP_VERSION environment variable. The path to the artifacts will be inside
|
|
* the packages/react-native/third-party folder.
|
|
*/
|
|
async function prepareReactNativeDependenciesArtifactsAsync(
|
|
version /*:string*/,
|
|
buildType /*: BuildFlavor */,
|
|
) /*: Promise<string> */ {
|
|
dependencyLog(`Preparing ReactNativeDependencies...`);
|
|
|
|
// Create artifacts folder
|
|
const artifactsPath /*: string*/ = path.resolve(process.cwd(), 'third-party');
|
|
|
|
// Ensure that the artifacts folder exists
|
|
fs.mkdirSync(artifactsPath, {recursive: true});
|
|
|
|
// Path for keeping track of the current version in the artifacts folder
|
|
const versionFilePath = path.join(artifactsPath, 'version.txt');
|
|
|
|
// Resolve the version from the environment variable or use the default version
|
|
let resolvedVersion = process.env.RN_DEP_VERSION ?? version;
|
|
|
|
if (resolvedVersion === 'nightly') {
|
|
dependencyLog('Using latest nightly tarball');
|
|
const rnVersion = await getNightlyVersionFromNPM();
|
|
resolvedVersion = rnVersion;
|
|
}
|
|
|
|
// Check if the ReactNativeDependencies artifacts are already downloaded
|
|
if (
|
|
checkExistingVersion(
|
|
versionFilePath,
|
|
resolvedVersion,
|
|
buildType,
|
|
artifactsPath,
|
|
)
|
|
) {
|
|
return artifactsPath;
|
|
}
|
|
|
|
const sourceType = await reactNativeDependenciesSourceType(
|
|
resolvedVersion,
|
|
buildType,
|
|
);
|
|
const localPath = await resolveSourceFromSourceType(
|
|
sourceType,
|
|
resolvedVersion,
|
|
buildType,
|
|
artifactsPath,
|
|
);
|
|
|
|
// Extract the tar.gz
|
|
const tmpPath = '/tmp/react-native-dependencies';
|
|
fs.mkdirSync(tmpPath, {recursive: true});
|
|
execSync(`tar -xzf "${localPath}" -C "${tmpPath}"`, {
|
|
stdio: 'inherit',
|
|
});
|
|
|
|
const xcframeworkSource = path.join(
|
|
tmpPath,
|
|
'packages',
|
|
'react-native',
|
|
'third-party',
|
|
'ReactNativeDependencies.xcframework',
|
|
);
|
|
// Copy the extracted files to the artifacts folder
|
|
execSync(`cp -R "${xcframeworkSource}" "${artifactsPath}"`, {
|
|
stdio: 'inherit',
|
|
});
|
|
|
|
// Delete the tarball after extraction
|
|
if (!process.env.HERMES_ENGINE_TARBALL_PATH) {
|
|
fs.unlinkSync(localPath);
|
|
}
|
|
|
|
return artifactsPath;
|
|
}
|
|
|
|
async function getNightlyVersionFromNPM() /*: Promise<string> */ {
|
|
const npmResponse /*: Response */ = await fetch(
|
|
'https://registry.npmjs.org/react-native/nightly',
|
|
);
|
|
|
|
if (!npmResponse.ok) {
|
|
throw new Error(
|
|
`Couldn't get an answer from NPM: ${npmResponse.status} ${npmResponse.statusText}`,
|
|
);
|
|
}
|
|
|
|
const json = await npmResponse.json();
|
|
const latestNightly = json.version;
|
|
dependencyLog(`Using version ${latestNightly}`);
|
|
return latestNightly;
|
|
}
|
|
|
|
/*::
|
|
type ReactNativeDependenciesEngineSourceType =
|
|
| 'download_prebuild_tarball'
|
|
| 'download_prebuilt_nightly_tarball'
|
|
*/
|
|
|
|
const ReactNativeDependenciesEngineSourceTypes = {
|
|
DOWNLOAD_PREBUILD_TARBALL: 'download_prebuild_tarball',
|
|
DOWNLOAD_PREBUILT_NIGHTLY_TARBALL: 'download_prebuilt_nightly_tarball',
|
|
} /*:: as const */;
|
|
|
|
/**
|
|
* Checks if the ReactNativeDependencies artifacts are already downloaded and up to date with the specified version.
|
|
* Returns true if the artifacts are up to date, false otherwise.
|
|
*/
|
|
function checkExistingVersion(
|
|
versionFilePath /*: string */,
|
|
version /*: string */,
|
|
buildType /*: BuildFlavor */,
|
|
artifactsPath /*: string */,
|
|
) {
|
|
const resolvedVersion = `${version}-${buildType}`;
|
|
const rndepXCFramework = path.join(
|
|
artifactsPath,
|
|
'ReactNativeDependencies.xcframework',
|
|
);
|
|
|
|
if (fs.existsSync(rndepXCFramework)) {
|
|
if (fs.existsSync(versionFilePath)) {
|
|
const versionFileContent = fs.readFileSync(versionFilePath, 'utf8');
|
|
dependencyLog(`Version found on disk: ${versionFileContent}`);
|
|
if (versionFileContent.trim() === resolvedVersion) {
|
|
dependencyLog(
|
|
`ReactNativeDependencies artifacts already downloaded and up to date: ${artifactsPath}`,
|
|
);
|
|
return true;
|
|
}
|
|
} else {
|
|
dependencyLog(
|
|
`React Native Dependencies found on disk at: ${artifactsPath}.\nNo version file has been found. We are going to use it anyway, but there might be some unexpected behaviors.`,
|
|
);
|
|
}
|
|
} else {
|
|
dependencyLog('React Native Dependencies not found on disk');
|
|
}
|
|
|
|
// If the version file does not exist or the version does not match, delete the artifacts folder
|
|
fs.rmSync(artifactsPath, {recursive: true, force: true});
|
|
|
|
// Lets create the version.txt file
|
|
fs.mkdirSync(artifactsPath, {recursive: true});
|
|
fs.writeFileSync(versionFilePath, resolvedVersion, 'utf8');
|
|
dependencyLog(
|
|
`ReactNativeDependencies artifacts folder created: ${artifactsPath} with version: ${resolvedVersion}`,
|
|
);
|
|
return false;
|
|
}
|
|
|
|
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';
|
|
const namespace = 'com/facebook/react';
|
|
return `${mavenRepoUrl}/${namespace}/react-native-artifacts/${version}/react-native-artifacts-${version}-reactnative-dependencies-${buildType.toLowerCase()}.tar.gz`;
|
|
}
|
|
|
|
async function getNightlyTarballUrl(
|
|
version /*: string */,
|
|
buildType /*: BuildFlavor */,
|
|
) /*: Promise<string> */ {
|
|
const coordinate = 'react-native-artifacts';
|
|
const artifactName = `reactnative-dependencies-${buildType.toLowerCase()}.tar.gz`;
|
|
|
|
return await computeNightlyTarballURL(
|
|
version,
|
|
buildType,
|
|
coordinate,
|
|
artifactName,
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Checks if a ReactNativeDependencies artifact exists at the given URL using fetch instead of curl
|
|
*/
|
|
async function reactNativeDependenciesArtifactExists(
|
|
tarballUrl /*: string */,
|
|
) /*: Promise<boolean> */ {
|
|
try {
|
|
const response /*: Response */ = await fetch(tarballUrl, {
|
|
method: 'HEAD',
|
|
});
|
|
|
|
return response.status === 200;
|
|
} catch (e) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Determines the source type for ReactNativeDependencies based on availability
|
|
*/
|
|
async function reactNativeDependenciesSourceType(
|
|
version /*: string */,
|
|
buildType /*: BuildFlavor */,
|
|
) /*: Promise<ReactNativeDependenciesEngineSourceType> */ {
|
|
const tarballUrl = getTarballUrl(version, buildType);
|
|
if (await reactNativeDependenciesArtifactExists(tarballUrl)) {
|
|
dependencyLog(`Using download prebuild ${buildType} tarball`);
|
|
return ReactNativeDependenciesEngineSourceTypes.DOWNLOAD_PREBUILD_TARBALL;
|
|
}
|
|
|
|
// For nightly tarball, we need to resolve redirects first
|
|
const nightlyUrl = await getNightlyTarballUrl(version, buildType);
|
|
if (await reactNativeDependenciesArtifactExists(nightlyUrl)) {
|
|
dependencyLog('Using download prebuild nightly tarball');
|
|
return ReactNativeDependenciesEngineSourceTypes.DOWNLOAD_PREBUILT_NIGHTLY_TARBALL;
|
|
}
|
|
|
|
dependencyLog(
|
|
'Using download prebuild nightly tarball - this is a fallback and might not work.',
|
|
);
|
|
return ReactNativeDependenciesEngineSourceTypes.DOWNLOAD_PREBUILT_NIGHTLY_TARBALL;
|
|
}
|
|
|
|
async function resolveSourceFromSourceType(
|
|
sourceType /*: ReactNativeDependenciesEngineSourceType */,
|
|
version /*: string */,
|
|
buildType /*: BuildFlavor */,
|
|
artifactsPath /*: string*/,
|
|
) /*: Promise<string> */ {
|
|
switch (sourceType) {
|
|
case ReactNativeDependenciesEngineSourceTypes.DOWNLOAD_PREBUILD_TARBALL:
|
|
return downloadPrebuildTarball(version, buildType, artifactsPath);
|
|
case ReactNativeDependenciesEngineSourceTypes.DOWNLOAD_PREBUILT_NIGHTLY_TARBALL:
|
|
return downloadPrebuiltNightlyTarball(version, buildType, artifactsPath);
|
|
default:
|
|
abort(
|
|
`[ReactNativeDependencies] Unsupported or invalid source type provided: ${sourceType}`,
|
|
);
|
|
return '';
|
|
}
|
|
}
|
|
|
|
async function downloadPrebuildTarball(
|
|
version /*: string */,
|
|
buildType /*: BuildFlavor */,
|
|
artifactsPath /*: string*/,
|
|
) /*: Promise<string> */ {
|
|
const url = getTarballUrl(version, buildType);
|
|
dependencyLog(`Using release tarball from URL: ${url}`);
|
|
return downloadStableReactNativeDependencies(
|
|
version,
|
|
buildType,
|
|
artifactsPath,
|
|
);
|
|
}
|
|
|
|
async function downloadPrebuiltNightlyTarball(
|
|
version /*: string */,
|
|
buildType /*: BuildFlavor */,
|
|
artifactsPath /*: string*/,
|
|
) /*: Promise<string> */ {
|
|
const url = await getNightlyTarballUrl(version, buildType);
|
|
dependencyLog(`Using nightly tarball from URL: ${url}`);
|
|
return downloadReactNativeDependenciesTarball(
|
|
url,
|
|
version,
|
|
buildType,
|
|
artifactsPath,
|
|
);
|
|
}
|
|
|
|
async function downloadStableReactNativeDependencies(
|
|
version /*: string */,
|
|
buildType /*: BuildFlavor */,
|
|
artifactsPath /*: string */,
|
|
) /*: Promise<string> */ {
|
|
const tarballUrl = getTarballUrl(version, buildType);
|
|
return downloadReactNativeDependenciesTarball(
|
|
tarballUrl,
|
|
version,
|
|
buildType,
|
|
artifactsPath,
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Downloads a ReactNativeDependencies tarball using fetch instead of curl
|
|
*/
|
|
async function downloadReactNativeDependenciesTarball(
|
|
tarballUrl /*: string */,
|
|
version /*: string */,
|
|
buildType /*: BuildFlavor */,
|
|
artifactsPath /*: string */,
|
|
) /*: Promise<string> */ {
|
|
const destPath = buildType
|
|
? `${artifactsPath}/reactnative-dependencies-${version}-${buildType}.tar.gz`
|
|
: `${artifactsPath}/reactnative-dependencies-${version}.tar.gz`;
|
|
|
|
if (!fs.existsSync(destPath)) {
|
|
const tmpFile = `${artifactsPath}/reactnative-dependencies.download`;
|
|
try {
|
|
fs.mkdirSync(artifactsPath, {recursive: true});
|
|
dependencyLog(
|
|
`Downloading ReactNativeDependencies tarball from ${tarballUrl}`,
|
|
);
|
|
|
|
const response /*: Response */ = await fetch(tarballUrl);
|
|
|
|
if (!response.ok) {
|
|
throw new Error(
|
|
`Failed to download: ${response.status} ${response.statusText}`,
|
|
);
|
|
}
|
|
|
|
// Create a write stream to the temporary file
|
|
const fileStream = fs.createWriteStream(tmpFile);
|
|
|
|
// Use Node.js stream pipeline to safely pipe the response body to the file
|
|
if (response.body) {
|
|
await pipeline(response.body, fileStream);
|
|
} else {
|
|
// For older fetch implementations that don't support response.body as a stream
|
|
const buffer = await response.arrayBuffer();
|
|
|
|
fs.writeFileSync(tmpFile, Buffer.from(buffer));
|
|
}
|
|
|
|
// Move the temporary file to the destination path
|
|
fs.renameSync(tmpFile, destPath);
|
|
} catch (e) {
|
|
// Clean up the temporary file if it exists
|
|
if (fs.existsSync(tmpFile)) {
|
|
fs.unlinkSync(tmpFile);
|
|
}
|
|
abort(
|
|
`Failed to download ReactNativeDependencies tarball from ${tarballUrl}: ${e.message}`,
|
|
);
|
|
}
|
|
}
|
|
return destPath;
|
|
}
|
|
|
|
function abort(message /*: string */) {
|
|
dependencyLog(message, 'error');
|
|
throw new Error(message);
|
|
}
|
|
|
|
module.exports = {
|
|
prepareReactNativeDependenciesArtifactsAsync,
|
|
};
|