Update test-e2e-local to use source monorepo packages for RNTestProject (#42899)

Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/42899

Updates the `test-e2e-local` script to bootstrap `/tmp/RNTestProject/` using the currently checked out repository as the source of truth for all monorepo packages (previously we only did this for the `react-native` package).

This enables release testers to validate a release **before** physically publishing new dependency versions via `yarn bump-all-updated-packages`.

We are able to reuse the `scripts/template/initialize.js` script that is currently used for E2E validation in CI. This sets up a local Verdaccio server during project install.

NOTE: The time taken for `Build packages` + Verdaccio isn't ideal, I may explore a way to reuse the published package state in a future diff. Until then, this extra time (~1 min) will still be much less pain than the `bump-all-updated-packages` + commit process loop.

Changelog:
[Internal] - Update test-e2e-local to use source monorepo packages for RNTestProject

Reviewed By: lunaleaps

Differential Revision: D53484510

fbshipit-source-id: 600a8a3257a4947d7738ab9d908d6549c38545e6
This commit is contained in:
Alex Hunt
2024-02-15 13:04:00 -08:00
committed by Facebook GitHub Bot
parent 15700626cc
commit e4135e9be5
6 changed files with 110 additions and 65 deletions
+81 -51
View File
@@ -13,26 +13,31 @@
const {retry} = require('../circleci/retry');
const forEachPackage = require('../monorepo/for-each-package');
const setupVerdaccio = require('./setup-verdaccio');
const {
VERDACCIO_SERVER_URL,
VERDACCIO_STORAGE_PATH,
setupVerdaccio,
} = require('./setup-verdaccio');
const {parseArgs} = require('@pkgjs/parseargs');
const chalk = require('chalk');
const {execSync} = require('child_process');
const path = require('path');
const REPO_ROOT = path.resolve(__dirname, '../..');
const NPM_REGISTRY_SERVER = 'http://localhost:4873';
const config = {
options: {
projectName: {type: 'string'},
templatePath: {type: 'string'},
directory: {type: 'string'},
verbose: {type: 'boolean', default: false},
help: {type: 'boolean'},
},
};
async function main() {
const {
values: {help, projectName, templatePath, directory},
values: {help, ...options},
} = parseArgs(config);
if (help) {
@@ -55,94 +60,119 @@ async function main() {
--projectName The name of the new React Native project.
--templatePath The absolute path to the folder containing the template.
--directory The absolute path to the target project directory.
--verbose Print additional output. Default: false.
`);
return;
}
const VERDACCIO_PID = setupVerdaccio();
await initNewProjectFromSource(options);
}
async function initNewProjectFromSource(
{
projectName,
templatePath,
directory,
verbose = false,
} /*: {projectName: string, templatePath: string, directory: string, verbose?: boolean} */,
) {
console.log('Starting local npm proxy (Verdaccio)');
const verdaccioPid = setupVerdaccio();
console.log('Done ✅');
try {
process.stdout.write('Bootstrapped Verdaccio \u2705\n');
process.stdout.write('Building packages...\n');
execSync('node ./scripts/build/build.js', {
cwd: REPO_ROOT,
stdio: [process.stdin, process.stdout, process.stderr],
stdio: 'inherit',
});
console.log('\nDone ✅');
process.stdout.write('Starting to publish every package...\n');
console.log('Publishing packages to local npm proxy\n');
forEachPackage(
(packageAbsolutePath, packageRelativePathFromRoot, packageManifest) => {
if (packageManifest.private) {
return;
}
const desc = `${packageManifest.name} (${packageRelativePathFromRoot})`;
process.stdout.write(
`${desc} ${chalk.dim('.').repeat(Math.max(0, 72 - desc.length))} `,
);
execSync(
`npm publish --registry ${NPM_REGISTRY_SERVER} --access public`,
`npm publish --registry ${VERDACCIO_SERVER_URL} --access public`,
{
cwd: packageAbsolutePath,
stdio: [process.stdin, process.stdout, process.stderr],
stdio: verbose ? 'inherit' : [process.stderr],
},
);
process.stdout.write(
`Published ${packageManifest.name} to proxy \u2705\n`,
);
process.stdout.write(chalk.reset.inverse.bold.green(' DONE ') + '\n');
},
);
console.log('\nDone ✅');
process.stdout.write('Published every package \u2705\n');
console.log('Running react-native init without install');
execSync(
`node cli.js init ${projectName} \
`node ./packages/react-native/cli.js init ${projectName} \
--directory ${directory} \
--template ${templatePath} \
--verbose \
--skip-install \
--yarn-config-options npmRegistryServer="${NPM_REGISTRY_SERVER}"`,
--yarn-config-options npmRegistryServer="${VERDACCIO_SERVER_URL}"`,
{
cwd: `${REPO_ROOT}/packages/react-native`,
stdio: [process.stdin, process.stdout, process.stderr],
// Avoid loading packages/react-native/react-native.config.js
cwd: REPO_ROOT,
stdio: verbose ? 'inherit' : [process.stderr],
},
);
process.stdout.write('Completed initialization of template app \u2705\n');
console.log('\nDone ✅');
process.stdout.write('Installing dependencies in template app folder...\n');
const options = {
cwd: directory,
stdio: [process.stdin, process.stdout, process.stderr],
};
execSync(
`yarn config set npmRegistryServer "${NPM_REGISTRY_SERVER}"`,
options,
);
execSync(
'yarn config set unsafeHttpWhitelist --json \'["localhost"]\'',
options,
);
const success = await retry('yarn', options, 3, 500, ['install']);
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');
console.log('Installing project dependencies');
await runYarnUsingProxy(directory);
console.log('Done ✅');
} catch (e) {
console.log('Failed ❌');
throw e;
} finally {
process.stdout.write(`Killing verdaccio. PID ${VERDACCIO_PID}...\n`);
execSync(`kill -9 ${VERDACCIO_PID}`);
process.stdout.write('Killed Verdaccio process \u2705\n');
console.log(`Cleanup: Killing Verdaccio process (PID: ${verdaccioPid})`);
execSync(`kill -9 ${verdaccioPid}`);
console.log('Done ✅');
console.log('Cleanup: Removing Verdaccio storage directory');
execSync(`rm -rf ${VERDACCIO_STORAGE_PATH}`);
console.log('Done ✅');
// TODO(huntie): Fix memory leak from `spawn` in `setupVerdaccio` (above
// kill command does not wait for kill success).
process.exit(0);
}
}
async function runYarnUsingProxy(cwd /*: string */) {
const execOptions = {
cwd,
stdio: 'inherit',
};
execSync(
`yarn config set npmRegistryServer "${VERDACCIO_SERVER_URL}"`,
execOptions,
);
execSync(
'yarn config set unsafeHttpWhitelist --json \'["localhost"]\'',
execOptions,
);
// TODO(huntie): Review pre-existing retry limit
const success = await retry('yarn', execOptions, 3, 500, ['install']);
if (!success) {
throw new Error('Failed to install project dependencies');
}
}
module.exports = {
initNewProjectFromSource,
};
if (require.main === module) {
// eslint-disable-next-line no-void
void main();
+5 -1
View File
@@ -48,4 +48,8 @@ function setupVerdaccio() /*: number */ {
return verdaccioProcess.pid;
}
module.exports = setupVerdaccio;
module.exports = {
setupVerdaccio,
VERDACCIO_SERVER_URL,
VERDACCIO_STORAGE_PATH,
};