mirror of
https://github.com/react/react.git
synced 2026-09-28 21:25:11 +08:00
## Summary `build-and-test.js` could feed three different commits into one release: `git archive main` (not `HEAD`), an interactively chosen React CI build, and `HEAD` saved as metadata. Firefox source review then could not reproduce the zip. Require a clean tree, resolve `HEAD` once, and use that hash for the source archive, the experimental React download, and the metadata printed for AMO. Drop the prompt that let those diverge. Depends on #37305 and #37306. ## How did you test this change? Build-script only. The release helper now errors on a dirty tree and threads a single `git rev-parse HEAD` into archive, download, and metadata. Co-authored-by: Ruslan Lesiutin <hoxy@meta.com>
242 lines
6.8 KiB
JavaScript
Executable File
242 lines
6.8 KiB
JavaScript
Executable File
#!/usr/bin/env node
|
|
|
|
'use strict';
|
|
|
|
const chalk = require('chalk');
|
|
const {exec} = require('child-process-promise');
|
|
const {homedir} = require('os');
|
|
const {join, relative} = require('path');
|
|
const {DRY_RUN, ROOT_PATH} = require('./configuration');
|
|
const {
|
|
clear,
|
|
confirm,
|
|
confirmContinue,
|
|
execRead,
|
|
logger,
|
|
saveBuildMetadata,
|
|
} = require('./utils');
|
|
|
|
// This is the primary control function for this script.
|
|
async function main() {
|
|
clear();
|
|
|
|
await confirm('Have you stopped all NPM DEV scripts?', () => {
|
|
const packagesPath = relative(process.cwd(), join(__dirname, 'packages'));
|
|
|
|
console.log('Stop all NPM DEV scripts in the following directories:');
|
|
console.log(
|
|
chalk.bold(' ' + join(packagesPath, 'react-devtools-core')),
|
|
chalk.gray('(start:backend, start:standalone)')
|
|
);
|
|
console.log(
|
|
chalk.bold(' ' + join(packagesPath, 'react-devtools-inline')),
|
|
chalk.gray('(start)')
|
|
);
|
|
|
|
const buildAndTestScriptPath = join(__dirname, 'build-and-test.js');
|
|
const pathToPrint = relative(process.cwd(), buildAndTestScriptPath);
|
|
|
|
console.log('\nThen restart this release step:');
|
|
console.log(chalk.bold.green(' ' + pathToPrint));
|
|
});
|
|
|
|
await confirm('Have you run the prepare-release script?', () => {
|
|
const prepareReleaseScriptPath = join(__dirname, 'prepare-release.js');
|
|
const pathToPrint = relative(process.cwd(), prepareReleaseScriptPath);
|
|
|
|
console.log('Begin by running the prepare-release script:');
|
|
console.log(chalk.bold.green(' ' + pathToPrint));
|
|
});
|
|
|
|
await ensureCleanWorkingTree();
|
|
const currentCommitHash = await getCurrentCommitHash();
|
|
const archivePath = await archiveGitRevision(currentCommitHash);
|
|
await downloadLatestReactBuild(currentCommitHash);
|
|
|
|
await buildAndTestInlinePackage();
|
|
await buildAndTestStandalonePackage();
|
|
await buildAndTestExtensions();
|
|
|
|
saveBuildMetadata({archivePath, currentCommitHash});
|
|
|
|
printFinalInstructions();
|
|
}
|
|
|
|
async function ensureCleanWorkingTree() {
|
|
const status = await execRead('git status --porcelain', {cwd: ROOT_PATH});
|
|
if (status !== '') {
|
|
throw new Error('Cannot build a release from a dirty working tree.');
|
|
}
|
|
}
|
|
|
|
async function getCurrentCommitHash() {
|
|
const commitHash = await execRead('git rev-parse HEAD', {cwd: ROOT_PATH});
|
|
if (commitHash === '') {
|
|
throw new Error('Failed to get current commit hash');
|
|
}
|
|
return commitHash;
|
|
}
|
|
|
|
async function archiveGitRevision(currentCommitHash) {
|
|
const desktopPath = join(homedir(), 'Desktop');
|
|
const archivePath = join(desktopPath, 'DevTools.tgz');
|
|
|
|
console.log(`Creating git archive at ${chalk.dim(archivePath)}`);
|
|
console.log('');
|
|
|
|
if (!DRY_RUN) {
|
|
await exec(
|
|
`git archive --format=tar.gz --output="${archivePath}" ${currentCommitHash}`,
|
|
{cwd: ROOT_PATH}
|
|
);
|
|
}
|
|
|
|
return archivePath;
|
|
}
|
|
|
|
async function buildAndTestExtensions() {
|
|
const extensionsPackagePath = join(
|
|
ROOT_PATH,
|
|
'packages',
|
|
'react-devtools-extensions'
|
|
);
|
|
const buildExtensionsPromise = exec('yarn build', {
|
|
cwd: extensionsPackagePath,
|
|
});
|
|
|
|
await logger(
|
|
buildExtensionsPromise,
|
|
`Building browser extensions ${chalk.dim('(this may take a minute)')}`,
|
|
{
|
|
estimate: 60000,
|
|
}
|
|
);
|
|
|
|
console.log('');
|
|
console.log(`Extensions have been build for Chrome, Edge, and Firefox.`);
|
|
console.log('');
|
|
console.log('Smoke test each extension before continuing:');
|
|
console.log(` ${chalk.bold.green('cd ' + extensionsPackagePath)}`);
|
|
console.log('');
|
|
console.log(` ${chalk.dim('# Test Chrome extension')}`);
|
|
console.log(` ${chalk.bold.green('yarn test:chrome')}`);
|
|
console.log('');
|
|
console.log(` ${chalk.dim('# Test Edge extension')}`);
|
|
console.log(` ${chalk.bold.green('yarn test:edge')}`);
|
|
console.log('');
|
|
console.log(` ${chalk.dim('# Firefox Chrome extension')}`);
|
|
console.log(` ${chalk.bold.green('yarn test:firefox')}`);
|
|
|
|
await confirmContinue();
|
|
}
|
|
|
|
async function buildAndTestStandalonePackage() {
|
|
const corePackagePath = join(ROOT_PATH, 'packages', 'react-devtools-core');
|
|
const corePackageDest = join(corePackagePath, 'dist');
|
|
|
|
await exec(`rm -rf ${corePackageDest}`);
|
|
const buildCorePromise = exec('yarn build', {cwd: corePackagePath});
|
|
|
|
await logger(
|
|
buildCorePromise,
|
|
`Building ${chalk.bold('react-devtools-core')} package.`,
|
|
{
|
|
estimate: 25000,
|
|
}
|
|
);
|
|
|
|
const standalonePackagePath = join(ROOT_PATH, 'packages', 'react-devtools');
|
|
const safariFixturePath = join(
|
|
ROOT_PATH,
|
|
'fixtures',
|
|
'devtools',
|
|
'standalone',
|
|
'index.html'
|
|
);
|
|
|
|
console.log('');
|
|
console.log(
|
|
`Test the ${chalk.bold('react-devtools-core')} target before continuing:`
|
|
);
|
|
console.log(` ${chalk.bold.green('cd ' + standalonePackagePath)}`);
|
|
console.log(` ${chalk.bold.green('yarn start')}`);
|
|
console.log('');
|
|
console.log(
|
|
'The following fixture can be useful for testing Safari integration:'
|
|
);
|
|
console.log(` ${chalk.dim(safariFixturePath)}`);
|
|
|
|
await confirmContinue();
|
|
}
|
|
|
|
async function buildAndTestInlinePackage() {
|
|
const inlinePackagePath = join(
|
|
ROOT_PATH,
|
|
'packages',
|
|
'react-devtools-inline'
|
|
);
|
|
const inlinePackageDest = join(inlinePackagePath, 'dist');
|
|
|
|
await exec(`rm -rf ${inlinePackageDest}`);
|
|
const buildPromise = exec('yarn build', {cwd: inlinePackagePath});
|
|
|
|
await logger(
|
|
buildPromise,
|
|
`Building ${chalk.bold('react-devtools-inline')} package.`,
|
|
{
|
|
estimate: 10000,
|
|
}
|
|
);
|
|
|
|
const shellPackagePath = join(ROOT_PATH, 'packages', 'react-devtools-shell');
|
|
|
|
console.log('');
|
|
console.log(`Built ${chalk.bold('react-devtools-inline')} target.`);
|
|
console.log('');
|
|
console.log('Test this build before continuing:');
|
|
console.log(` ${chalk.bold.green('cd ' + shellPackagePath)}`);
|
|
console.log(` ${chalk.bold.green('yarn start')}`);
|
|
|
|
await confirmContinue();
|
|
}
|
|
|
|
async function downloadLatestReactBuild(currentCommitHash) {
|
|
const releaseScriptPath = join(ROOT_PATH, 'scripts', 'release');
|
|
const installPromise = exec('yarn install', {cwd: releaseScriptPath});
|
|
|
|
await logger(
|
|
installPromise,
|
|
`Installing release script dependencies. ${chalk.dim(
|
|
'(this may take a minute if CI is still running)'
|
|
)}`,
|
|
{
|
|
estimate: 5000,
|
|
}
|
|
);
|
|
|
|
console.log('');
|
|
|
|
const downloadScriptPath = join(
|
|
releaseScriptPath,
|
|
'download-experimental-build.js'
|
|
);
|
|
const downloadPromise = execRead(
|
|
`"${downloadScriptPath}" --commit=${currentCommitHash}`
|
|
);
|
|
|
|
await logger(downloadPromise, 'Downloading React artifacts from CI.', {
|
|
estimate: 15000,
|
|
});
|
|
}
|
|
|
|
function printFinalInstructions() {
|
|
const publishReleaseScriptPath = join(__dirname, 'publish-release.js');
|
|
const pathToPrint = relative(process.cwd(), publishReleaseScriptPath);
|
|
|
|
console.log('');
|
|
console.log('Continue by running the publish-release script:');
|
|
console.log(chalk.bold.green(' ' + pathToPrint));
|
|
}
|
|
|
|
main();
|