diff --git a/scripts/circleci/retry.js b/scripts/circleci/retry.js new file mode 100644 index 00000000000000..57649b7ea45c18 --- /dev/null +++ b/scripts/circleci/retry.js @@ -0,0 +1,41 @@ +/** + * 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. + * + * @format + * @oncall react-native + */ + +const {spawnSync} = require('child_process'); + +async function retry(command, options, maxRetries, delay, args) { + for (let i = 1; i <= maxRetries; i++) { + console.log(`Attempt ${i}: ${command}`); + const result = spawnSync(command, args ? args : [], options); + + if (result.status === 0) { + console.log(`Command succeeded on attempt ${i}`); + return true; + } + + console.log(`Command failed on attempt ${i}`); + + if (i >= maxRetries) { + console.log('Maximum retries reached. Exiting.'); + return false; + } + + if (delay > 0) { + console.log(`Sleeping for ${delay} seconds...`); + await new Promise(resolve => setTimeout(resolve, delay)); + } + + console.log('Retrying...'); + } +} + +module.exports = { + retry, +}; diff --git a/scripts/circleci/run_with_retry.js b/scripts/circleci/run_with_retry.js index a771521167983b..ca81dd81f6abe2 100755 --- a/scripts/circleci/run_with_retry.js +++ b/scripts/circleci/run_with_retry.js @@ -7,25 +7,17 @@ * @format */ -const {spawnSync} = require('child_process'); +const {retry} = require('./retry'); -function retryCommand(maxRetries, command) { - for (let i = 1; i <= maxRetries; i++) { - console.log(`Attempt ${i}: ${command}`); - const result = spawnSync(command, {shell: true, stdio: 'inherit'}); - - if (result.status === 0) { - console.log(`Command succeeded on attempt ${i}`); - process.exit(0); - } else { - console.log(`Command failed on attempt ${i}`); - if (i < maxRetries) { - console.log('Retrying...'); - } else { - console.log('Maximum retries reached. Exiting.'); - process.exit(1); - } - } +async function retryCommand(maxRetries, command) { + const success = await retry( + command, + {shell: true, stdio: 'inherit'}, + maxRetries, + 0, + ); + if (!success) { + process.exit(1); } } diff --git a/scripts/template/initialize.js b/scripts/template/initialize.js index 462e5a0e136928..f04907fe838f73 100644 --- a/scripts/template/initialize.js +++ b/scripts/template/initialize.js @@ -10,10 +10,11 @@ 'use strict'; const yargs = require('yargs'); -const {execSync, spawnSync} = require('child_process'); +const {execSync} = require('child_process'); const forEachPackage = require('../monorepo/for-each-package'); const setupVerdaccio = require('../setup-verdaccio'); +const {retry} = require('../circleci/retry'); const {argv} = yargs .option('r', { @@ -42,54 +43,69 @@ const {reactNativeRootPath, templateName, templateConfigPath, directory} = argv; const VERDACCIO_CONFIG_PATH = `${reactNativeRootPath}/.circleci/verdaccio.yml`; -function install() { +async function install() { const VERDACCIO_PID = setupVerdaccio( reactNativeRootPath, VERDACCIO_CONFIG_PATH, ); - process.stdout.write('Bootstrapped Verdaccio \u2705\n'); - - process.stdout.write('Starting to publish every package...\n'); - forEachPackage( - (packageAbsolutePath, packageRelativePathFromRoot, packageManifest) => { - if (packageManifest.private) { - return; - } - - execSync('npm publish --registry http://localhost:4873 --access public', { - cwd: packageAbsolutePath, + try { + process.stdout.write('Bootstrapped Verdaccio \u2705\n'); + + process.stdout.write('Starting to publish every package...\n'); + forEachPackage( + (packageAbsolutePath, packageRelativePathFromRoot, packageManifest) => { + if (packageManifest.private) { + return; + } + + execSync( + 'npm publish --registry http://localhost:4873 --access public', + { + cwd: packageAbsolutePath, + stdio: [process.stdin, process.stdout, process.stderr], + }, + ); + + process.stdout.write( + `Published ${packageManifest.name} to proxy \u2705\n`, + ); + }, + ); + + process.stdout.write('Published every package \u2705\n'); + + execSync( + `node cli.js init ${templateName} --directory ${directory} --template ${templateConfigPath} --verbose --skip-install`, + { + cwd: `${reactNativeRootPath}/packages/react-native`, stdio: [process.stdin, process.stdout, process.stderr], - }); - - process.stdout.write( - `Published ${packageManifest.name} to proxy \u2705\n`, - ); - }, - ); - - process.stdout.write('Published every package \u2705\n'); + }, + ); + process.stdout.write('Completed initialization of template app \u2705\n'); - execSync( - `node cli.js init ${templateName} --directory ${directory} --template ${templateConfigPath} --verbose --skip-install`, - { - cwd: `${reactNativeRootPath}/packages/react-native`, + process.stdout.write('Installing dependencies in template app folder...\n'); + const options = { + cwd: directory, stdio: [process.stdin, process.stdout, process.stderr], - }, - ); - process.stdout.write('Completed initialization of template app \u2705\n'); - - process.stdout.write('Installing dependencies in template app folder...\n'); - spawnSync('yarn', ['install'], { - cwd: directory, - stdio: [process.stdin, process.stdout, process.stderr], - }); - process.stdout.write('Installed dependencies via Yarn \u2705\n'); + }; + const success = await retry('yarn', options, 3, 500, ['install']); - process.stdout.write(`Killing verdaccio. PID — ${VERDACCIO_PID}...\n`); - execSync(`kill -9 ${VERDACCIO_PID}`); - process.stdout.write('Killed Verdaccio process \u2705\n'); - - process.exit(); + if (!success) { + process.stdout.write( + 'Failed to install dependencies in template app folder.', + ); + throw new Error('Failed to install dependencies in template app folder.'); + } + + process.stdout.write('Installed dependencies via Yarn \u2705\n'); + } finally { + process.stdout.write(`Killing verdaccio. PID — ${VERDACCIO_PID}...\n`); + execSync(`kill -9 ${VERDACCIO_PID}`); + process.stdout.write('Killed Verdaccio process \u2705\n'); + } } -install(); +install().then(() => { + console.log('Done with preparing the project.'); + process.exit(); +});