diff --git a/packages/react-native/scripts/hermes/hermes-utils.js b/packages/react-native/scripts/hermes/hermes-utils.js index 5ee9f73e624cbe..3a5aa8dcb3388f 100644 --- a/packages/react-native/scripts/hermes/hermes-utils.js +++ b/packages/react-native/scripts/hermes/hermes-utils.js @@ -4,6 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * + * @flow strict-local * @format */ @@ -14,6 +15,10 @@ const fs = require('fs'); const os = require('os'); const path = require('path'); +/*:: +type BuildType = 'dry-run' | 'release' | 'nightly' | 'prealpha'; +*/ + const SDKS_DIR = path.normalize(path.join(__dirname, '..', '..', 'sdks')); const HERMES_DIR = path.join(SDKS_DIR, 'hermes'); const HERMES_TAG_FILE_PATH = path.join(SDKS_DIR, '.hermesversion'); @@ -34,11 +39,15 @@ const MACOS_IMPORT_HERMESC_PATH = path.join( * @param args Array of arguments pass to the command. * @param options child process options. */ -function delegateSync(command, args, options) { +function delegateSync( + command /*: string */, + args /*: (Array | child_process$spawnSyncOpts) */, + options /*: ?child_process$spawnSyncOpts */, +) { return spawnSync(command, args, {stdio: 'inherit', ...options}); } -function readHermesTag() { +function readHermesTag() /*: string */ { if (fs.existsSync(HERMES_TAG_FILE_PATH)) { const data = fs .readFileSync(HERMES_TAG_FILE_PATH, { @@ -57,7 +66,7 @@ function readHermesTag() { return 'main'; } -function setHermesTag(hermesTag) { +function setHermesTag(hermesTag /*: string */) { if (readHermesTag() === hermesTag) { // No need to update. return; @@ -70,7 +79,7 @@ function setHermesTag(hermesTag) { console.log('Hermes tag has been updated. Please commit your changes.'); } -function getHermesTagSHA(hermesTag) { +function getHermesTagSHA(hermesTag /*: string */) /*: string */ { return execSync( `git ls-remote https://github.com/facebook/hermes ${hermesTag} | cut -f 1`, ) @@ -78,7 +87,7 @@ function getHermesTagSHA(hermesTag) { .trim(); } -function getHermesTarballDownloadPath(hermesTag) { +function getHermesTarballDownloadPath(hermesTag /*: string */) /*: string */ { const hermesTagSHA = getHermesTagSHA(hermesTag); return path.join(HERMES_TARBALL_DOWNLOAD_DIR, `hermes-${hermesTagSHA}.tgz`); } @@ -184,11 +193,13 @@ function isTestingAgainstLocalHermesTarball() { return 'HERMES_ENGINE_TARBALL_PATH' in process.env; } -function shouldBuildHermesFromSource(isInCI) { +function shouldBuildHermesFromSource(isInCI /*: boolean */) /*: boolean */ { return !isTestingAgainstLocalHermesTarball() && isInCI; } -function shouldUsePrebuiltHermesC(platform) { +function shouldUsePrebuiltHermesC( + platform /*: 'macos' | 'windows' */, +) /*: boolean */ { if (platform === 'macos') { return fs.existsSync(MACOS_HERMESC_PATH); } @@ -212,7 +223,9 @@ set_target_properties(native-hermesc PROPERTIES } } -function getHermesPrebuiltArtifactsTarballName(buildType) { +function getHermesPrebuiltArtifactsTarballName( + buildType /*: string */, +) /*: string */ { if (!buildType) { throw Error('Did not specify build type.'); } @@ -222,17 +235,20 @@ function getHermesPrebuiltArtifactsTarballName(buildType) { /** * Creates a tarball with the contents of the supplied directory. */ -function createTarballFromDirectory(directory, filename) { +function createTarballFromDirectory( + directory /*: string */, + filename /*: string */, +) { const args = ['-C', directory, '-czvf', filename, '.']; delegateSync('tar', args); } function createHermesPrebuiltArtifactsTarball( - hermesDir, - buildType, - tarballOutputDir, - excludeDebugSymbols, -) { + hermesDir /*: string */, + buildType /*: string */, + tarballOutputDir /*: string */, + excludeDebugSymbols /*: boolean */, +) /*: string */ { validateHermesFrameworksExist(path.join(hermesDir, 'destroot')); if (!fs.existsSync(tarballOutputDir)) { @@ -282,7 +298,7 @@ function createHermesPrebuiltArtifactsTarball( return tarballFilename; } -function validateHermesFrameworksExist(destrootDir) { +function validateHermesFrameworksExist(destrootDir /*: string */) { if ( !fs.existsSync( path.join(destrootDir, 'Library/Frameworks/macosx/hermes.framework'), diff --git a/scripts/release-utils.js b/scripts/release-utils.js index d31b5c81b19539..3f6fcabb839590 100644 --- a/scripts/release-utils.js +++ b/scripts/release-utils.js @@ -4,6 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * + * @flow strict-local * @format */ @@ -14,7 +15,11 @@ const { } = require('../packages/react-native/scripts/hermes/hermes-utils'); const {echo, env, exec, exit, popd, pushd, test} = require('shelljs'); -function generateAndroidArtifacts(releaseVersion) { +/*:: +type BuildType = 'dry-run' | 'release' | 'nightly' | 'prealpha'; +*/ + +function generateAndroidArtifacts(releaseVersion /*: string */) { // -------- Generating Android Artifacts echo('Generating Android artifacts inside /tmp/maven-local'); if (exec('./gradlew publishAllToMavenTempLocal').code) { @@ -52,10 +57,15 @@ function generateAndroidArtifacts(releaseVersion) { }); } -function publishAndroidArtifactsToMaven(releaseVersion, buildType) { +function publishAndroidArtifactsToMaven( + releaseVersion /*: string */, + buildType /*: BuildType */, +) { // -------- Publish every artifact to Maven Central // The GPG key is base64 encoded on CircleCI and then decoded here + // $FlowFixMe[prop-missing] let buff = Buffer.from(env.ORG_GRADLE_PROJECT_SIGNING_KEY_ENCODED, 'base64'); + // $FlowFixMe[prop-missing] env.ORG_GRADLE_PROJECT_SIGNING_KEY = buff.toString('ascii'); // We want to gate ourselves against accidentally publishing a 1.x or a 1000.x on @@ -76,7 +86,9 @@ function publishAndroidArtifactsToMaven(releaseVersion, buildType) { const isSnapshot = buildType === 'nightly' || buildType === 'prealpha'; // -------- For nightly releases, we only need to publish the snapshot to Sonatype snapshot repo. if ( - exec('./gradlew publishAllToSonatype -PisSnapshot=' + isSnapshot).code + exec( + './gradlew publishAllToSonatype -PisSnapshot=' + isSnapshot.toString(), + ).code ) { echo('Failed to publish artifacts to Sonatype (Maven Central)'); exit(1); @@ -87,11 +99,11 @@ function publishAndroidArtifactsToMaven(releaseVersion, buildType) { } function generateiOSArtifacts( - jsiFolder, - hermesCoreSourceFolder, - buildType, - targetFolder, -) { + jsiFolder /*: string */, + hermesCoreSourceFolder /*: string */, + buildType /*: 'Debug' | string */, + targetFolder /*: string */, +) /*: string */ { pushd(`${hermesCoreSourceFolder}`); //Generating iOS Artifacts @@ -115,7 +127,7 @@ function generateiOSArtifacts( return tarballOutputPath; } -function failIfTagExists(version, buildType) { +function failIfTagExists(version /*: string */, buildType /*: BuildType */) { // When dry-run in stable branch, the tag already exists. // We are bypassing the tag-existence check when in a dry-run to have the CI pass if (buildType === 'dry-run') { @@ -130,7 +142,7 @@ function failIfTagExists(version, buildType) { } } -function checkIfTagExists(version) { +function checkIfTagExists(version /*: string */) { const {code, stdout} = exec('git tag -l', {silent: true}); if (code !== 0) { throw new Error('Failed to retrieve the list of tags'); diff --git a/scripts/test-e2e-local-clean.js b/scripts/test-e2e-local-clean.js index b86a8a43b23445..896e8b9fc6a9f6 100644 --- a/scripts/test-e2e-local-clean.js +++ b/scripts/test-e2e-local-clean.js @@ -4,6 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * + * @flow strict-local * @format */ diff --git a/scripts/test-e2e-local.js b/scripts/test-e2e-local.js index a8938751a402f9..a20c093ddcd6ea 100644 --- a/scripts/test-e2e-local.js +++ b/scripts/test-e2e-local.js @@ -4,6 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * + * @flow strict-local * @format */ @@ -28,6 +29,10 @@ const path = require('path'); const {cd, exec, popd, pushd, pwd, sed} = require('shelljs'); const yargs = require('yargs'); +/* :: +type Unwrap = T extends Promise ? U : T; +*/ + const argv = yargs .option('t', { alias: 'target', @@ -59,17 +64,20 @@ const argv = yargs * - @circleCIArtifacts manager object to manage all the download of CircleCIArtifacts. If null, it will fallback not to use them. * - @onReleaseBranch whether we are on a release branch or not */ -async function testRNTesterIOS(circleCIArtifacts, onReleaseBranch) { +async function testRNTesterIOS( + circleCIArtifacts /*: Unwrap> */, + onReleaseBranch /*: boolean */, +) { console.info( `We're going to test the ${ - argv.hermes ? 'Hermes' : 'JSC' + argv.hermes === true ? 'Hermes' : 'JSC' } version of RNTester iOS with the new Architecture enabled`, ); // remember that for this to be successful // you should have run bundle install once // in your local setup - if (argv.hermes && circleCIArtifacts != null) { + if (argv.hermes === true && circleCIArtifacts != null) { const hermesURL = await circleCIArtifacts.artifactURLHermesDebug(); const hermesPath = path.join( circleCIArtifacts.baseTmpPath(), @@ -84,14 +92,14 @@ async function testRNTesterIOS(circleCIArtifacts, onReleaseBranch) { } else { exec( `USE_HERMES=${ - argv.hermes ? 1 : 0 - } CI=${onReleaseBranch} RCT_NEW_ARCH_ENABLED=1 bundle exec pod install --ansi`, + argv.hermes === true ? 1 : 0 + } CI=${onReleaseBranch.toString()} RCT_NEW_ARCH_ENABLED=1 bundle exec pod install --ansi`, ); } // if everything succeeded so far, we can launch Metro and the app // start the Metro server in a separate window - launchPackagerInSeparateWindow(pwd()); + launchPackagerInSeparateWindow(pwd().toString()); // launch the app on iOS simulator exec('npx react-native run-ios --scheme RNTester --simulator "iPhone 14"'); @@ -103,17 +111,19 @@ async function testRNTesterIOS(circleCIArtifacts, onReleaseBranch) { * Parameters: * - @circleCIArtifacts manager object to manage all the download of CircleCIArtifacts. If null, it will fallback not to use them. */ -async function testRNTesterAndroid(circleCIArtifacts) { +async function testRNTesterAndroid( + circleCIArtifacts /*: Unwrap> */, +) { maybeLaunchAndroidEmulator(); console.info( `We're going to test the ${ - argv.hermes ? 'Hermes' : 'JSC' + argv.hermes === true ? 'Hermes' : 'JSC' } version of RNTester Android with the new Architecture enabled`, ); // Start the Metro server so it will be ready if the app can be built and installed successfully. - launchPackagerInSeparateWindow(pwd()); + launchPackagerInSeparateWindow(pwd().toString()); // Wait for the Android Emulator to be properly loaded and bootstrapped exec( @@ -127,9 +137,10 @@ async function testRNTesterAndroid(circleCIArtifacts) { ); const emulatorArch = exec('adb shell getprop ro.product.cpu.abi').trim(); - const rntesterAPKURL = argv.hermes - ? await circleCIArtifacts.artifactURLForHermesRNTesterAPK(emulatorArch) - : await circleCIArtifacts.artifactURLForJSCRNTesterAPK(emulatorArch); + const rntesterAPKURL = + argv.hermes === true + ? await circleCIArtifacts.artifactURLForHermesRNTesterAPK(emulatorArch) + : await circleCIArtifacts.artifactURLForJSCRNTesterAPK(emulatorArch); console.info('Start Downloading APK'); circleCIArtifacts.downloadArtifact(rntesterAPKURL, downloadPath); @@ -138,7 +149,7 @@ async function testRNTesterAndroid(circleCIArtifacts) { } else { exec( `../../gradlew :packages:rn-tester:android:app:${ - argv.hermes ? 'installHermesDebug' : 'installJscDebug' + argv.hermes === true ? 'installHermesDebug' : 'installJscDebug' } --quiet`, ); } @@ -161,7 +172,10 @@ async function testRNTesterAndroid(circleCIArtifacts) { * - @circleCIArtifacts manager object to manage all the download of CircleCIArtifacts. If null, it will fallback not to use them. * - @onReleaseBranch whether we are on a release branch or not */ -async function testRNTester(circleCIArtifacts, onReleaseBranch) { +async function testRNTester( + circleCIArtifacts /*:Unwrap> */, + onReleaseBranch /*: boolean */, +) { // FIXME: make sure that the commands retains colors // (--ansi) doesn't always work // see also https://github.com/shelljs/shelljs/issues/86 @@ -177,7 +191,9 @@ async function testRNTester(circleCIArtifacts, onReleaseBranch) { // === RNTestProject === // -async function testRNTestProject(circleCIArtifacts) { +async function testRNTestProject( + circleCIArtifacts /*: Unwrap> */, +) { console.info("We're going to test a fresh new RN project"); // create the local npm package to feed the CLI @@ -195,7 +211,7 @@ async function testRNTestProject(circleCIArtifacts) { const buildType = 'dry-run'; // Prepare some variables for later use - const repoRoot = pwd(); + const repoRoot = pwd().toString(); const reactNativePackagePath = `${repoRoot}/packages/react-native`; const localNodeTGZPath = `${reactNativePackagePath}/react-native-${releaseVersion}.tgz`; @@ -239,7 +255,7 @@ async function testRNTestProject(circleCIArtifacts) { ); // Update gradle properties to set Hermes as false - if (!argv.hermes) { + if (argv.hermes == null) { sed( '-i', 'hermesEnabled=true', @@ -254,7 +270,7 @@ async function testRNTestProject(circleCIArtifacts) { exec('bundle install'); exec( `HERMES_ENGINE_TARBALL_PATH=${hermesPath} USE_HERMES=${ - argv.hermes ? 1 : 0 + argv.hermes === true ? 1 : 0 } bundle exec pod install --ansi`, ); @@ -286,7 +302,8 @@ async function main() { const onReleaseBranch = branchName.endsWith('-stable'); let circleCIArtifacts = await setupCircleCIArtifacts( - argv.circleciToken, + // $FlowIgnoreError[prop-missing] + argv.circleCIToken, branchName, ); @@ -297,4 +314,5 @@ async function main() { } } +// $FlowIgnoreError[unused-promise] main(); diff --git a/scripts/testing-utils.js b/scripts/testing-utils.js index 8588a9bba164b3..78b81c8b6de47e 100644 --- a/scripts/testing-utils.js +++ b/scripts/testing-utils.js @@ -4,11 +4,14 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * + * @flow strict-local * @format */ 'use strict'; +import type {ShellString} from 'shelljs'; + const { downloadHermesSourceTarball, expandHermesSourceTarball, @@ -19,11 +22,16 @@ const { generateiOSArtifacts, } = require('./release-utils'); const fs = require('fs'); +// $FlowIgnore[cannot-resolve-module] const {spawn} = require('node:child_process'); const os = require('os'); const path = require('path'); const {cp, exec} = require('shelljs'); +/*:: +type BuildType = 'dry-run' | 'release' | 'nightly' | 'prealpha'; +*/ + /* * Android related utils - leverages android tooling */ @@ -32,24 +40,29 @@ const {cp, exec} = require('shelljs'); // here's the reference folder: // https://github.com/react-native-community/cli/blob/main/packages/cli-platform-android/src/commands/runAndroid -const emulatorCommand = process.env.ANDROID_HOME - ? `${process.env.ANDROID_HOME}/emulator/emulator` - : 'emulator'; +const emulatorCommand = + process.env.ANDROID_HOME != null + ? `${process.env.ANDROID_HOME}/emulator/emulator` + : 'emulator'; const getEmulators = () => { const emulatorsOutput = exec(`${emulatorCommand} -list-avds`).stdout; return emulatorsOutput.split(os.EOL).filter(name => name !== ''); }; -const launchEmulator = emulatorName => { +const launchEmulator = (emulatorName /*: string */) => { // we need both options 'cause reasons: // from docs: "When using the detached option to start a long-running process, the process will not stay running in the background after the parent exits unless it is provided with a stdio configuration that is not connected to the parent. If the parent's stdio is inherited, the child will remain attached to the controlling terminal." // here: https://nodejs.org/api/child_process.html#optionsdetached - const child_process = spawn(emulatorCommand, [`@${emulatorName}`], { - detached: true, - stdio: 'ignore', - }); + const child_process /*: child_process$ChildProcess */ = spawn( + emulatorCommand, + [`@${emulatorName}`], + { + detached: true, + stdio: 'ignore', + }, + ); child_process.unref(); }; @@ -107,8 +120,8 @@ function maybeLaunchAndroidEmulator() { // inspired by CLI again https://github.com/react-native-community/cli/blob/main/packages/cli-tools/src/isPackagerRunning.ts function isPackagerRunning( - packagerPort = process.env.RCT_METRO_PORT || '8081', -) { + packagerPort /*: string */ = process.env.RCT_METRO_PORT ?? '8081', +) /*: 'running' | 'unrecognized' | 'not_running' */ { try { const status = exec(`curl http://localhost:${packagerPort}/status`, { silent: true, @@ -121,7 +134,7 @@ function isPackagerRunning( } // this is a very limited implementation of how this should work -function launchPackagerInSeparateWindow(folderPath) { +function launchPackagerInSeparateWindow(folderPath /*: string */) { const command = `tell application "Terminal" to do script "cd ${folderPath} && yarn start"`; exec(`osascript -e '${command}' >/dev/null < */ { + if (circleciToken == null) { return null; } @@ -161,9 +177,9 @@ async function setupCircleCIArtifacts(circleciToken, branchName) { } async function downloadArtifactsFromCircleCI( - circleCIArtifacts, - mavenLocalPath, - localNodeTGZPath, + circleCIArtifacts /*: typeof circleCIArtifactsUtils */, + mavenLocalPath /*: string */, + localNodeTGZPath /*: string */, ) { const mavenLocalURL = await circleCIArtifacts.artifactURLForMavenLocal(); const hermesURL = await circleCIArtifacts.artifactURLHermesDebug(); @@ -187,9 +203,9 @@ async function downloadArtifactsFromCircleCI( } function buildArtifactsLocally( - releaseVersion, - buildType, - reactNativePackagePath, + releaseVersion /*: string */, + buildType /*: BuildType */, + reactNativePackagePath /*: string */, ) { // this is needed to generate the Android artifacts correctly const exitCode = exec( @@ -264,13 +280,13 @@ function buildArtifactsLocally( * - @hermesPath the path to hermes for iOS */ async function prepareArtifacts( - circleCIArtifacts, - mavenLocalPath, - localNodeTGZPath, - releaseVersion, - buildType, - reactNativePackagePath, -) { + circleCIArtifacts /*: ?typeof circleCIArtifactsUtils */, + mavenLocalPath /*: string */, + localNodeTGZPath /*: string */, + releaseVersion /*: string */, + buildType /*: BuildType */, + reactNativePackagePath /*: string */, +) /*: Promise */ { return circleCIArtifacts != null ? await downloadArtifactsFromCircleCI( circleCIArtifacts, diff --git a/scripts/update-template-package.js b/scripts/update-template-package.js index b3203f4c7dd299..be6abec363c3e5 100755 --- a/scripts/update-template-package.js +++ b/scripts/update-template-package.js @@ -4,6 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * + * @flow strict-local * @format */ @@ -20,11 +21,12 @@ const path = require('path'); * `dependencyMap` is a dict of package name to its version * ex. {"react-native": "0.23.0", "other-dep": "nightly"} */ -function updateTemplatePackage(dependencyMap) { +function updateTemplatePackage(dependencyMap /*: {[string]: string} */) { const jsonPath = path.join( __dirname, '../packages/react-native/template/package.json', ); + // $FlowIgnore[unsupported-syntax] const templatePackageJson = require(jsonPath); const updatedPackageJson = applyPackageVersions(